-
Notifications
You must be signed in to change notification settings - Fork 77
/
Copy pathscoring_panel.go
158 lines (144 loc) · 4.57 KB
/
scoring_panel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright 2014 Team 254. All Rights Reserved.
// Author: [email protected] (Patrick Fairbank)
//
// Web handlers for scoring interface.
package web
import (
"fmt"
"github.com/Team254/cheesy-arena/field"
"github.com/Team254/cheesy-arena/game"
"github.com/Team254/cheesy-arena/model"
"github.com/Team254/cheesy-arena/websocket"
"github.com/mitchellh/mapstructure"
"io"
"log"
"net/http"
)
// Renders the scoring interface which enables input of scores in real-time.
func (web *Web) scoringPanelHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsAdmin(w, r) {
return
}
alliance := r.PathValue("alliance")
if alliance != "red" && alliance != "blue" {
handleWebErr(w, fmt.Errorf("Invalid alliance '%s'.", alliance))
return
}
template, err := web.parseFiles("templates/scoring_panel.html", "templates/base.html")
if err != nil {
handleWebErr(w, err)
return
}
data := struct {
*model.EventSettings
PlcIsEnabled bool
Alliance string
}{web.arena.EventSettings, web.arena.Plc.IsEnabled(), alliance}
err = template.ExecuteTemplate(w, "base_no_navbar", data)
if err != nil {
handleWebErr(w, err)
return
}
}
// The websocket endpoint for the scoring interface client to send control commands and receive status updates.
func (web *Web) scoringPanelWebsocketHandler(w http.ResponseWriter, r *http.Request) {
if !web.userIsAdmin(w, r) {
return
}
alliance := r.PathValue("alliance")
if alliance != "red" && alliance != "blue" {
handleWebErr(w, fmt.Errorf("Invalid alliance '%s'.", alliance))
return
}
var realtimeScore **field.RealtimeScore
if alliance == "red" {
realtimeScore = &web.arena.RedRealtimeScore
} else {
realtimeScore = &web.arena.BlueRealtimeScore
}
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
handleWebErr(w, err)
return
}
defer ws.Close()
web.arena.ScoringPanelRegistry.RegisterPanel(alliance, ws)
web.arena.ScoringStatusNotifier.Notify()
defer web.arena.ScoringStatusNotifier.Notify()
defer web.arena.ScoringPanelRegistry.UnregisterPanel(alliance, ws)
// Subscribe the websocket to the notifiers whose messages will be passed on to the client, in a separate goroutine.
go ws.HandleNotifiers(web.arena.MatchLoadNotifier, web.arena.MatchTimeNotifier, web.arena.RealtimeScoreNotifier,
web.arena.ReloadDisplaysNotifier)
// Loop, waiting for commands and responding to them, until the client closes the connection.
for {
command, data, err := ws.Read()
if err != nil {
if err == io.EOF {
// Client has closed the connection; nothing to do here.
return
}
log.Println(err)
return
}
score := &(*realtimeScore).CurrentScore
scoreChanged := false
if command == "commitMatch" {
if web.arena.MatchState != field.PostMatch {
// Don't allow committing the score until the match is over.
ws.WriteError("Cannot commit score: Match is not over.")
continue
}
web.arena.ScoringPanelRegistry.SetScoreCommitted(alliance, ws)
web.arena.ScoringStatusNotifier.Notify()
} else {
args := struct {
TeamPosition int
StageIndex int
}{}
err = mapstructure.Decode(data, &args)
if err != nil {
ws.WriteError(err.Error())
continue
}
switch command {
case "leave":
if args.TeamPosition >= 1 && args.TeamPosition <= 3 {
score.LeaveStatuses[args.TeamPosition-1] = !score.LeaveStatuses[args.TeamPosition-1]
scoreChanged = true
}
case "onStage":
if args.TeamPosition >= 1 && args.TeamPosition <= 3 && args.StageIndex >= 0 && args.StageIndex <= 2 {
endgameStatus := game.EndgameStatus(args.StageIndex + 2)
if score.EndgameStatuses[args.TeamPosition-1] == endgameStatus {
score.EndgameStatuses[args.TeamPosition-1] = game.EndgameNone
} else {
score.EndgameStatuses[args.TeamPosition-1] = endgameStatus
}
scoreChanged = true
}
case "park":
if args.TeamPosition >= 1 && args.TeamPosition <= 3 {
if score.EndgameStatuses[args.TeamPosition-1] == game.EndgameParked {
score.EndgameStatuses[args.TeamPosition-1] = game.EndgameNone
} else {
score.EndgameStatuses[args.TeamPosition-1] = game.EndgameParked
}
scoreChanged = true
}
case "microphone":
if args.StageIndex >= 0 && args.StageIndex <= 2 {
score.MicrophoneStatuses[args.StageIndex] = !score.MicrophoneStatuses[args.StageIndex]
scoreChanged = true
}
case "trap":
if args.StageIndex >= 0 && args.StageIndex <= 2 {
score.TrapStatuses[args.StageIndex] = !score.TrapStatuses[args.StageIndex]
scoreChanged = true
}
}
if scoreChanged {
web.arena.RealtimeScoreNotifier.Notify()
}
}
}
}