Refactor display registry to not broadcast all config changes to all displays.

This commit is contained in:
Patrick Fairbank
2020-03-29 16:55:53 -07:00
parent 305db8e0f2
commit f12d37778e
24 changed files with 188 additions and 177 deletions

View File

@@ -40,7 +40,7 @@ func (web *Web) allianceStationDisplayWebsocketHandler(w http.ResponseWriter, r
handleWebErr(w, err)
return
}
defer web.arena.MarkDisplayDisconnected(display)
defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id)
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
@@ -50,7 +50,7 @@ func (web *Web) allianceStationDisplayWebsocketHandler(w http.ResponseWriter, r
defer ws.Close()
// Subscribe the websocket to the notifiers whose messages will be passed on to the client.
ws.HandleNotifiers(web.arena.MatchTimingNotifier, web.arena.AllianceStationDisplayModeNotifier,
ws.HandleNotifiers(display.Notifier, web.arena.MatchTimingNotifier, web.arena.AllianceStationDisplayModeNotifier,
web.arena.ArenaStatusNotifier, web.arena.MatchLoadNotifier, web.arena.MatchTimeNotifier,
web.arena.RealtimeScoreNotifier, web.arena.DisplayConfigurationNotifier, web.arena.ReloadDisplaysNotifier)
web.arena.RealtimeScoreNotifier, web.arena.ReloadDisplaysNotifier)
}

View File

@@ -36,13 +36,13 @@ func TestAllianceStationDisplayWebsocket(t *testing.T) {
ws := websocket.NewTestWebsocket(conn)
// Should get a few status updates right after connection.
readWebsocketType(t, ws, "displayConfiguration")
readWebsocketType(t, ws, "matchTiming")
readWebsocketType(t, ws, "allianceStationDisplayMode")
readWebsocketType(t, ws, "arenaStatus")
readWebsocketType(t, ws, "matchLoad")
readWebsocketType(t, ws, "matchTime")
readWebsocketType(t, ws, "realtimeScore")
readWebsocketType(t, ws, "displayConfiguration")
// Change to a different screen.
web.arena.AllianceStationDisplayMode = "logo"

View File

@@ -40,7 +40,7 @@ func (web *Web) announcerDisplayWebsocketHandler(w http.ResponseWriter, r *http.
handleWebErr(w, err)
return
}
defer web.arena.MarkDisplayDisconnected(display)
defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id)
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
@@ -50,7 +50,7 @@ func (web *Web) announcerDisplayWebsocketHandler(w http.ResponseWriter, r *http.
defer ws.Close()
// Subscribe the websocket to the notifiers whose messages will be passed on to the client.
ws.HandleNotifiers(web.arena.MatchTimingNotifier, web.arena.MatchLoadNotifier, web.arena.MatchTimeNotifier,
web.arena.RealtimeScoreNotifier, web.arena.ScorePostedNotifier, web.arena.AudienceDisplayModeNotifier,
web.arena.DisplayConfigurationNotifier, web.arena.ReloadDisplaysNotifier)
ws.HandleNotifiers(display.Notifier, web.arena.MatchTimingNotifier, web.arena.MatchLoadNotifier,
web.arena.MatchTimeNotifier, web.arena.RealtimeScoreNotifier, web.arena.ScorePostedNotifier,
web.arena.AudienceDisplayModeNotifier, web.arena.ReloadDisplaysNotifier)
}

View File

@@ -29,13 +29,13 @@ func TestAnnouncerDisplayWebsocket(t *testing.T) {
ws := websocket.NewTestWebsocket(conn)
// Should get a few status updates right after connection.
readWebsocketType(t, ws, "displayConfiguration")
readWebsocketType(t, ws, "matchTiming")
readWebsocketType(t, ws, "matchLoad")
readWebsocketType(t, ws, "matchTime")
readWebsocketType(t, ws, "realtimeScore")
readWebsocketType(t, ws, "scorePosted")
readWebsocketType(t, ws, "audienceDisplayMode")
readWebsocketType(t, ws, "displayConfiguration")
web.arena.MatchLoadNotifier.Notify()
readWebsocketType(t, ws, "matchLoad")

View File

@@ -43,7 +43,7 @@ func (web *Web) audienceDisplayWebsocketHandler(w http.ResponseWriter, r *http.R
handleWebErr(w, err)
return
}
defer web.arena.MarkDisplayDisconnected(display)
defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id)
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
@@ -53,8 +53,8 @@ func (web *Web) audienceDisplayWebsocketHandler(w http.ResponseWriter, r *http.R
defer ws.Close()
// Subscribe the websocket to the notifiers whose messages will be passed on to the client.
ws.HandleNotifiers(web.arena.MatchTimingNotifier, web.arena.AudienceDisplayModeNotifier,
ws.HandleNotifiers(display.Notifier, web.arena.MatchTimingNotifier, web.arena.AudienceDisplayModeNotifier,
web.arena.MatchLoadNotifier, web.arena.MatchTimeNotifier, web.arena.RealtimeScoreNotifier,
web.arena.PlaySoundNotifier, web.arena.ScorePostedNotifier, web.arena.AllianceSelectionNotifier,
web.arena.LowerThirdNotifier, web.arena.DisplayConfigurationNotifier, web.arena.ReloadDisplaysNotifier)
web.arena.LowerThirdNotifier, web.arena.ReloadDisplaysNotifier)
}

View File

@@ -36,6 +36,7 @@ func TestAudienceDisplayWebsocket(t *testing.T) {
ws := websocket.NewTestWebsocket(conn)
// Should get a few status updates right after connection.
readWebsocketType(t, ws, "displayConfiguration")
readWebsocketType(t, ws, "matchTiming")
readWebsocketType(t, ws, "audienceDisplayMode")
readWebsocketType(t, ws, "matchLoad")
@@ -44,7 +45,6 @@ func TestAudienceDisplayWebsocket(t *testing.T) {
readWebsocketType(t, ws, "scorePosted")
readWebsocketType(t, ws, "allianceSelection")
readWebsocketType(t, ws, "lowerThird")
readWebsocketType(t, ws, "displayConfiguration")
// Run through a match cycle.
web.arena.MatchLoadNotifier.Notify()

View File

@@ -54,18 +54,16 @@ func (web *Web) enforceDisplayConfiguration(w http.ResponseWriter, r *http.Reque
// Constructs, registers, and returns the display object for the given incoming websocket request.
func (web *Web) registerDisplay(r *http.Request) (*field.Display, error) {
display, err := field.DisplayFromUrl(r.URL.Path, r.URL.Query())
displayConfig, err := field.DisplayFromUrl(r.URL.Path, r.URL.Query())
if err != nil {
return nil, err
}
// Extract the source IP address of the request and store it in the display object.
if ipAddress := r.Header.Get("X-Real-IP"); ipAddress != "" {
display.IpAddress = ipAddress
} else {
display.IpAddress = regexp.MustCompile("(.*):\\d+$").FindStringSubmatch(r.RemoteAddr)[1]
var ipAddress string
if ipAddress = r.Header.Get("X-Real-IP"); ipAddress == "" {
ipAddress = regexp.MustCompile("(.*):\\d+$").FindStringSubmatch(r.RemoteAddr)[1]
}
web.arena.RegisterDisplay(display)
return display, nil
return web.arena.RegisterDisplay(displayConfig, ipAddress), nil
}

View File

@@ -39,7 +39,7 @@ func (web *Web) fieldMonitorDisplayWebsocketHandler(w http.ResponseWriter, r *ht
handleWebErr(w, err)
return
}
defer web.arena.MarkDisplayDisconnected(display)
defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id)
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
@@ -49,6 +49,5 @@ func (web *Web) fieldMonitorDisplayWebsocketHandler(w http.ResponseWriter, r *ht
defer ws.Close()
// Subscribe the websocket to the notifiers whose messages will be passed on to the client.
ws.HandleNotifiers(web.arena.ArenaStatusNotifier, web.arena.DisplayConfigurationNotifier,
web.arena.ReloadDisplaysNotifier)
ws.HandleNotifiers(display.Notifier, web.arena.ArenaStatusNotifier, web.arena.ReloadDisplaysNotifier)
}

View File

@@ -29,6 +29,6 @@ func TestFieldMonitorDisplayWebsocket(t *testing.T) {
ws := websocket.NewTestWebsocket(conn)
// Should get a few status updates right after connection.
readWebsocketType(t, ws, "arenaStatus")
readWebsocketType(t, ws, "displayConfiguration")
readWebsocketType(t, ws, "arenaStatus")
}

View File

@@ -39,7 +39,7 @@ func (web *Web) pitDisplayWebsocketHandler(w http.ResponseWriter, r *http.Reques
handleWebErr(w, err)
return
}
defer web.arena.MarkDisplayDisconnected(display)
defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id)
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
@@ -49,6 +49,5 @@ func (web *Web) pitDisplayWebsocketHandler(w http.ResponseWriter, r *http.Reques
defer ws.Close()
// Subscribe the websocket to the notifiers whose messages will be passed on to the client.
ws.HandleNotifiers(web.arena.EventStatusNotifier, web.arena.DisplayConfigurationNotifier,
web.arena.ReloadDisplaysNotifier)
ws.HandleNotifiers(display.Notifier, web.arena.EventStatusNotifier, web.arena.ReloadDisplaysNotifier)
}

View File

@@ -29,8 +29,8 @@ func TestPitDisplayWebsocket(t *testing.T) {
ws := websocket.NewTestWebsocket(conn)
// Should get a few status updates right after connection.
readWebsocketType(t, ws, "eventStatus")
readWebsocketType(t, ws, "displayConfiguration")
readWebsocketType(t, ws, "eventStatus")
// Check forced reloading as that is the only purpose the pit websocket serves.
web.arena.ReloadDisplaysNotifier.Notify()

View File

@@ -39,7 +39,7 @@ func (web *Web) placeholderDisplayWebsocketHandler(w http.ResponseWriter, r *htt
handleWebErr(w, err)
return
}
defer web.arena.MarkDisplayDisconnected(display)
defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id)
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
@@ -49,5 +49,5 @@ func (web *Web) placeholderDisplayWebsocketHandler(w http.ResponseWriter, r *htt
defer ws.Close()
// Subscribe the websocket to the notifiers whose messages will be passed on to the client.
ws.HandleNotifiers(web.arena.DisplayConfigurationNotifier, web.arena.ReloadDisplaysNotifier)
ws.HandleNotifiers(display.Notifier, web.arena.ReloadDisplaysNotifier)
}

View File

@@ -37,15 +37,15 @@ func TestPlaceholderDisplayWebsocket(t *testing.T) {
readWebsocketType(t, ws, "displayConfiguration")
if assert.Contains(t, web.arena.Displays, "123") {
assert.Equal(t, "blop", web.arena.Displays["123"].Nickname)
if assert.Equal(t, 1, len(web.arena.Displays["123"].Configuration)) {
assert.Equal(t, "b", web.arena.Displays["123"].Configuration["a"])
assert.Equal(t, "blop", web.arena.Displays["123"].DisplayConfiguration.Nickname)
if assert.Equal(t, 1, len(web.arena.Displays["123"].DisplayConfiguration.Configuration)) {
assert.Equal(t, "b", web.arena.Displays["123"].DisplayConfiguration.Configuration["a"])
}
}
// Reconfigure the display and verify that the new configuration is received.
display := &field.Display{Id: "123", Nickname: "Alliance", Type: field.AllianceStationDisplay,
displayConfig := field.DisplayConfiguration{Id: "123", Nickname: "Alliance", Type: field.AllianceStationDisplay,
Configuration: map[string]string{"station": "B2"}}
web.arena.UpdateDisplay(display)
web.arena.UpdateDisplay(displayConfig)
readWebsocketType(t, ws, "displayConfiguration")
}

View File

@@ -69,7 +69,7 @@ func (web *Web) queueingDisplayWebsocketHandler(w http.ResponseWriter, r *http.R
handleWebErr(w, err)
return
}
defer web.arena.MarkDisplayDisconnected(display)
defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id)
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
@@ -79,6 +79,6 @@ func (web *Web) queueingDisplayWebsocketHandler(w http.ResponseWriter, r *http.R
defer ws.Close()
// Subscribe the websocket to the notifiers whose messages will be passed on to the client.
ws.HandleNotifiers(web.arena.MatchTimingNotifier, web.arena.MatchLoadNotifier, web.arena.MatchTimeNotifier,
web.arena.EventStatusNotifier, web.arena.DisplayConfigurationNotifier, web.arena.ReloadDisplaysNotifier)
ws.HandleNotifiers(display.Notifier, web.arena.MatchTimingNotifier, web.arena.MatchLoadNotifier,
web.arena.MatchTimeNotifier, web.arena.EventStatusNotifier, web.arena.ReloadDisplaysNotifier)
}

View File

@@ -29,9 +29,9 @@ func TestQueueingDisplayWebsocket(t *testing.T) {
ws := websocket.NewTestWebsocket(conn)
// Should get a few status updates right after connection.
readWebsocketType(t, ws, "displayConfiguration")
readWebsocketType(t, ws, "matchTiming")
readWebsocketType(t, ws, "matchLoad")
readWebsocketType(t, ws, "matchTime")
readWebsocketType(t, ws, "eventStatus")
readWebsocketType(t, ws, "displayConfiguration")
}

View File

@@ -68,13 +68,13 @@ func (web *Web) displaysWebsocketHandler(w http.ResponseWriter, r *http.Request)
switch messageType {
case "configureDisplay":
var display field.Display
err = mapstructure.Decode(data, &display)
var displayConfig field.DisplayConfiguration
err = mapstructure.Decode(data, &displayConfig)
if err != nil {
ws.WriteError(err.Error())
continue
}
if err = web.arena.UpdateDisplay(&display); err != nil {
if err = web.arena.UpdateDisplay(displayConfig); err != nil {
ws.WriteError(err.Error())
continue
}

View File

@@ -32,37 +32,37 @@ func TestSetupDisplaysWebsocket(t *testing.T) {
// Should get a few status updates right after connection.
message := readDisplayConfiguration(t, ws)
assert.Empty(t, message.Displays)
assert.Empty(t, message.DisplayUrls)
assert.Empty(t, message)
// Connect a couple of displays and verify the resulting configuration messages.
displayConn1, _, _ := gorillawebsocket.DefaultDialer.Dial(wsUrl+"/display/websocket?displayId=1", nil)
defer displayConn1.Close()
displayWs1 := websocket.NewTestWebsocket(displayConn1)
assert.Equal(t, "/display?displayId=1", readWebsocketType(t, displayWs1, "displayConfiguration"))
readDisplayConfiguration(t, ws)
displayConn2, _, _ := gorillawebsocket.DefaultDialer.Dial(wsUrl+
"/displays/alliance_station/websocket?displayId=2&station=R2", nil)
defer displayConn2.Close()
expectedDisplay1 := &field.Display{Id: "1", Type: field.PlaceholderDisplay, Configuration: map[string]string{},
ConnectionCount: 1, IpAddress: "127.0.0.1"}
expectedDisplay2 := &field.Display{Id: "2", Type: field.AllianceStationDisplay,
Configuration: map[string]string{"station": "R2"}, ConnectionCount: 1, IpAddress: "127.0.0.1"}
message = readDisplayConfiguration(t, ws)
if assert.Equal(t, 2, len(message.Displays)) {
assert.Equal(t, expectedDisplay1, message.Displays["1"])
assert.Equal(t, expectedDisplay2, message.Displays["2"])
assert.Equal(t, expectedDisplay1.ToUrl(), message.DisplayUrls["1"])
assert.Equal(t, expectedDisplay2.ToUrl(), message.DisplayUrls["2"])
if assert.Equal(t, 2, len(message)) {
assert.Equal(t, field.DisplayConfiguration{"1", "", field.PlaceholderDisplay, map[string]string{}},
message["1"].DisplayConfiguration)
assert.Equal(t, 1, message["1"].ConnectionCount)
assert.Equal(t, "127.0.0.1", message["1"].IpAddress)
assert.Equal(t, field.DisplayConfiguration{"2", "", field.AllianceStationDisplay,
map[string]string{"station": "R2"}}, message["2"].DisplayConfiguration)
assert.Equal(t, 1, message["2"].ConnectionCount)
assert.Equal(t, "127.0.0.1", message["2"].IpAddress)
}
// Reconfigure a display and verify the result.
expectedDisplay1.Nickname = "Audience Display"
expectedDisplay1.Type = field.AudienceDisplay
expectedDisplay1.Configuration["background"] = "#00f"
expectedDisplay1.Configuration["reversed"] = "true"
ws.Write("configureDisplay", expectedDisplay1)
displayConfig := field.DisplayConfiguration{Id: "1", Nickname: "Audience Display", Type: field.AudienceDisplay,
Configuration: map[string]string{"background": "#00f", "reversed": "true"}}
ws.Write("configureDisplay", displayConfig)
message = readDisplayConfiguration(t, ws)
assert.Equal(t, expectedDisplay1, message.Displays["1"])
assert.Equal(t, expectedDisplay1.ToUrl(), message.DisplayUrls["1"])
assert.Equal(t, displayConfig, message["1"].DisplayConfiguration)
assert.Equal(t, "/displays/audience?displayId=1&nickname=Audience+Display&background=%2300f&reversed=true",
readWebsocketType(t, displayWs1, "displayConfiguration"))
}
func TestSetupDisplaysWebsocketReloadDisplays(t *testing.T) {
@@ -82,7 +82,7 @@ func TestSetupDisplaysWebsocketReloadDisplays(t *testing.T) {
displayConn, _, _ := gorillawebsocket.DefaultDialer.Dial(wsUrl+"/display/websocket?displayId=1", nil)
defer displayConn.Close()
displayWs := websocket.NewTestWebsocket(displayConn)
readDisplayConfiguration(t, displayWs)
assert.Equal(t, "/display?displayId=1", readWebsocketType(t, displayWs, "displayConfiguration"))
readDisplayConfiguration(t, ws)
// Reset a display selectively and verify the resulting message.
@@ -92,10 +92,10 @@ func TestSetupDisplaysWebsocketReloadDisplays(t *testing.T) {
assert.Equal(t, nil, readWebsocketType(t, displayWs, "reload"))
}
func readDisplayConfiguration(t *testing.T, ws *websocket.Websocket) *field.DisplayConfigurationMessage {
func readDisplayConfiguration(t *testing.T, ws *websocket.Websocket) map[string]field.Display {
message := readWebsocketType(t, ws, "displayConfiguration")
var displayConfigurationMessage field.DisplayConfigurationMessage
var displayConfigurationMessage map[string]field.Display
err := mapstructure.Decode(message, &displayConfigurationMessage)
assert.Nil(t, err)
return &displayConfigurationMessage
return displayConfigurationMessage
}

View File

@@ -39,7 +39,7 @@ func (web *Web) twitchDisplayWebsocketHandler(w http.ResponseWriter, r *http.Req
handleWebErr(w, err)
return
}
defer web.arena.MarkDisplayDisconnected(display)
defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id)
ws, err := websocket.NewWebsocket(w, r)
if err != nil {
@@ -49,5 +49,5 @@ func (web *Web) twitchDisplayWebsocketHandler(w http.ResponseWriter, r *http.Req
defer ws.Close()
// Subscribe the websocket to the notifiers whose messages will be passed on to the client.
ws.HandleNotifiers(web.arena.DisplayConfigurationNotifier, web.arena.ReloadDisplaysNotifier)
ws.HandleNotifiers(display.Notifier, web.arena.ReloadDisplaysNotifier)
}