From 6a62372b669e1ce6bbc3e7a39119d932a6afe6a6 Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:27:03 -0500 Subject: [PATCH 01/40] Update match pause duration --- game/match_timing.go | 2 +- model/event_settings_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/game/match_timing.go b/game/match_timing.go index 080728bd..117b98ae 100644 --- a/game/match_timing.go +++ b/game/match_timing.go @@ -15,7 +15,7 @@ var MatchTiming = struct { WarningRemainingDurationSec int TimeoutDurationSec int TimeoutWarningRemainingDurationSec int -}{0, 15, 2, 135, 30, 0, 60} +}{0, 15, 3, 135, 30, 0, 60} func GetDurationToAutoEnd() time.Duration { return time.Duration(MatchTiming.WarmupDurationSec+MatchTiming.AutoDurationSec) * time.Second diff --git a/model/event_settings_test.go b/model/event_settings_test.go index f4d393e9..b16591e9 100644 --- a/model/event_settings_test.go +++ b/model/event_settings_test.go @@ -29,7 +29,7 @@ func TestEventSettingsReadWrite(t *testing.T) { ApAdminWpaKey: "1234Five", WarmupDurationSec: 0, AutoDurationSec: 15, - PauseDurationSec: 2, + PauseDurationSec: 3, TeleopDurationSec: 135, WarningRemainingDurationSec: 30, }, From f514c0e5a8d2dc5bb4308d32579b90900663d43a Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:46:41 -0500 Subject: [PATCH 02/40] Some minor refactoring Changes from upstream --- field/arena.go | 2 ++ field/arena_notifiers.go | 28 ++++++++++++++-------------- field/display.go | 2 +- field/driver_station_connection.go | 10 ++-------- partner/tba.go | 4 ++-- plc/plc.go | 2 +- static/js/match_play.js | 4 ++-- web/field_monitor_display_test.go | 8 ++++---- web/match_play.go | 4 ++-- web/match_play_test.go | 10 +++++----- web/setup_lower_thirds_test.go | 2 +- web/web.go | 4 ++-- web/web_test.go | 6 +++--- websocket/notifier.go | 10 +++++----- websocket/notifier_test.go | 4 ++-- websocket/websocket.go | 12 ++++++------ websocket/websocket_test.go | 6 +++--- 17 files changed, 57 insertions(+), 61 deletions(-) diff --git a/field/arena.go b/field/arena.go index 7d4a24ff..cac081d2 100755 --- a/field/arena.go +++ b/field/arena.go @@ -491,6 +491,7 @@ func (arena *Arena) Update() { enabled = true } } + arena.FieldReset = false case PausePeriod: auto = false enabled = false @@ -525,6 +526,7 @@ func (arena *Arena) Update() { arena.preLoadNextMatch() }() } + arena.FieldReset = false case TimeoutActive: if matchTimeSec >= float64(game.MatchTiming.TimeoutDurationSec) { arena.MatchState = PostTimeout diff --git a/field/arena_notifiers.go b/field/arena_notifiers.go index 48edf0e5..4a4633f7 100755 --- a/field/arena_notifiers.go +++ b/field/arena_notifiers.go @@ -66,15 +66,15 @@ func (arena *Arena) configureNotifiers() { arena.SCCNotifier = websocket.NewNotifier("sccstatus", arena.generateSCCStatusMessage) } -func (arena *Arena) generateAllianceSelectionMessage() interface{} { +func (arena *Arena) generateAllianceSelectionMessage() any { return &arena.AllianceSelectionAlliances } -func (arena *Arena) generateAllianceStationDisplayModeMessage() interface{} { +func (arena *Arena) generateAllianceStationDisplayModeMessage() any { return arena.AllianceStationDisplayMode } -func (arena *Arena) generateArenaStatusMessage() interface{} { +func (arena *Arena) generateArenaStatusMessage() any { // Convert AP team wifi network status array to a map by station for ease of client use. teamWifiStatuses := make(map[string]network.TeamWifiStatus) for i, station := range []string{"R1", "R2", "R3", "B1", "B2", "B3"} { @@ -112,11 +112,11 @@ func (arena *Arena) generateArenaStatusMessage() interface{} { arena.Scc.IsSccConnected("blue")} } -func (arena *Arena) generateAudienceDisplayModeMessage() interface{} { +func (arena *Arena) generateAudienceDisplayModeMessage() any { return arena.AudienceDisplayMode } -func (arena *Arena) generateDisplayConfigurationMessage() interface{} { +func (arena *Arena) generateDisplayConfigurationMessage() any { // Notify() for this notifier must always called from a method that has a lock on the display mutex. // Make a copy of the map to avoid potential data races; otherwise the same map would get iterated through as it is // serialized to JSON, outside the mutex lock. @@ -127,18 +127,18 @@ func (arena *Arena) generateDisplayConfigurationMessage() interface{} { return displaysCopy } -func (arena *Arena) generateEventStatusMessage() interface{} { +func (arena *Arena) generateEventStatusMessage() any { return arena.EventStatus } -func (arena *Arena) generateLowerThirdMessage() interface{} { +func (arena *Arena) generateLowerThirdMessage() any { return &struct { LowerThird *model.LowerThird ShowLowerThird bool }{arena.LowerThird, arena.ShowLowerThird} } -func (arena *Arena) generateMatchLoadMessage() interface{} { +func (arena *Arena) generateMatchLoadMessage() any { teams := make(map[string]*model.Team) for station, allianceStation := range arena.AllianceStations { teams[station] = allianceStation.Team @@ -187,15 +187,15 @@ func (arena *Arena) generateMatchLoadMessage() interface{} { } } -func (arena *Arena) generateMatchTimeMessage() interface{} { +func (arena *Arena) generateMatchTimeMessage() any { return MatchTimeMessage{arena.MatchState, int(arena.MatchTimeSec())} } -func (arena *Arena) generateMatchTimingMessage() interface{} { +func (arena *Arena) generateMatchTimingMessage() any { return &game.MatchTiming } -func (arena *Arena) generateRealtimeScoreMessage() interface{} { +func (arena *Arena) generateRealtimeScoreMessage() any { fields := struct { Red *audienceAllianceScoreFields Blue *audienceAllianceScoreFields @@ -207,11 +207,11 @@ func (arena *Arena) generateRealtimeScoreMessage() interface{} { return &fields } -func (arena *Arena) generateSCCStatusMessage() interface{} { +func (arena *Arena) generateSCCStatusMessage() any { return arena.Scc.GenerateNotifierStatus() } -func (arena *Arena) generateScorePostedMessage() interface{} { +func (arena *Arena) generateScorePostedMessage() any { // For elimination matches, summarize the state of the series. var seriesStatus, seriesLeader string var matchup *bracket.Matchup @@ -244,7 +244,7 @@ func (arena *Arena) generateScorePostedMessage() interface{} { } } -func (arena *Arena) generateFieldLightsMessage() interface{} { +func (arena *Arena) generateFieldLightsMessage() any { return &struct { Lights string }{arena.FieldLights.GetCurrentStateAsString()} diff --git a/field/display.go b/field/display.go index b3c3c1a6..1d764e78 100644 --- a/field/display.go +++ b/field/display.go @@ -139,7 +139,7 @@ func (display *Display) ToUrl() string { return builder.String() } -func (display *Display) generateDisplayConfigurationMessage() interface{} { +func (display *Display) generateDisplayConfigurationMessage() any { return display.ToUrl() } diff --git a/field/driver_station_connection.go b/field/driver_station_connection.go index 21f26b28..6b8bcb77 100644 --- a/field/driver_station_connection.go +++ b/field/driver_station_connection.go @@ -223,15 +223,9 @@ func (dsConn *DriverStationConnection) encodeControlPacket(arena *Arena) [22]byt // Remaining number of seconds in match. var matchSecondsRemaining int switch arena.MatchState { - case PreMatch: - fallthrough - case TimeoutActive: - fallthrough - case PostTimeout: + case PreMatch, TimeoutActive, PostTimeout: matchSecondsRemaining = game.MatchTiming.AutoDurationSec - case StartMatch: - fallthrough - case AutoPeriod: + case StartMatch, AutoPeriod: matchSecondsRemaining = game.MatchTiming.AutoDurationSec - int(arena.MatchTimeSec()) case PausePeriod: matchSecondsRemaining = game.MatchTiming.TeleopDurationSec diff --git a/partner/tba.go b/partner/tba.go index f80f4fab..fae740af 100755 --- a/partner/tba.go +++ b/partner/tba.go @@ -95,8 +95,8 @@ type TbaEvent struct { } type TbaMediaItem struct { - Details map[string]interface{} `json:"details"` - Type string `json:"type"` + Details map[string]any `json:"details"` + Type string `json:"type"` } type TbaPublishedAward struct { diff --git a/plc/plc.go b/plc/plc.go index 34546851..35b8b165 100644 --- a/plc/plc.go +++ b/plc/plc.go @@ -336,7 +336,7 @@ func (plc *Plc) writeCoils() bool { return true } -func (plc *Plc) generateIoChangeMessage() interface{} { +func (plc *Plc) generateIoChangeMessage() any { return &struct { Inputs []bool Registers []uint16 diff --git a/static/js/match_play.js b/static/js/match_play.js index efed94f3..3e77c4e1 100755 --- a/static/js/match_play.js +++ b/static/js/match_play.js @@ -179,8 +179,8 @@ var handleArenaStatus = function(data) { $("#matchStartReason").html(data.CanStartMatchReason); } $("#abortMatch").prop("disabled", true); - $("#signalVolunteers").prop("disabled", true); - $("#signalReset").prop("disabled", true); + $("#signalVolunteers").prop("disabled", false); + $("#signalReset").prop("disabled", false); $("#commitResults").prop("disabled", true); $("#discardResults").prop("disabled", true); $("#editResults").prop("disabled", true); diff --git a/web/field_monitor_display_test.go b/web/field_monitor_display_test.go index 35b707e9..48a8bfd3 100644 --- a/web/field_monitor_display_test.go +++ b/web/field_monitor_display_test.go @@ -37,7 +37,7 @@ func TestFieldMonitorDisplayWebsocket(t *testing.T) { readWebsocketType(t, ws, "eventStatus") // Should not be able to update team notes. - ws.Write("updateTeamNotes", map[string]interface{}{"station": "B1", "notes": "Bypassed in M1"}) + ws.Write("updateTeamNotes", map[string]any{"station": "B1", "notes": "Bypassed in M1"}) assert.Contains(t, readWebsocketError(t, ws), "Must be in FTA mode to update team notes") assert.Equal(t, "", web.arena.AllianceStations["B1"].Team.FtaNotes) } @@ -61,13 +61,13 @@ func TestFieldMonitorFtaDisplayWebsocket(t *testing.T) { readWebsocketType(t, ws, "eventStatus") // Should not be able to update team notes. - ws.Write("updateTeamNotes", map[string]interface{}{"station": "B1", "notes": "Bypassed in M1"}) + ws.Write("updateTeamNotes", map[string]any{"station": "B1", "notes": "Bypassed in M1"}) readWebsocketType(t, ws, "arenaStatus") assert.Equal(t, "Bypassed in M1", web.arena.AllianceStations["B1"].Team.FtaNotes) // Check error scenarios. - ws.Write("updateTeamNotes", map[string]interface{}{"station": "N", "notes": "Bypassed in M2"}) + ws.Write("updateTeamNotes", map[string]any{"station": "N", "notes": "Bypassed in M2"}) assert.Contains(t, readWebsocketError(t, ws), "Invalid alliance station") - ws.Write("updateTeamNotes", map[string]interface{}{"station": "R3", "notes": "Bypassed in M3"}) + ws.Write("updateTeamNotes", map[string]any{"station": "R3", "notes": "Bypassed in M3"}) assert.Contains(t, readWebsocketError(t, ws), "No team present") } diff --git a/web/match_play.go b/web/match_play.go index b5694031..9c8b978d 100755 --- a/web/match_play.go +++ b/web/match_play.go @@ -288,14 +288,14 @@ func (web *Web) matchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request continue } case "signalVolunteers": - if web.arena.MatchState != field.PostMatch { + if web.arena.MatchState != field.PostMatch && web.arena.MatchState != field.PreMatch { // Don't allow clearing the field until the match is over. continue } web.arena.FieldVolunteers = true continue // Don't reload. case "signalReset": - if web.arena.MatchState != field.PostMatch { + if web.arena.MatchState != field.PostMatch && web.arena.MatchState != field.PreMatch { // Don't allow clearing the field until the match is over. continue } diff --git a/web/match_play_test.go b/web/match_play_test.go index 8081aa85..35d77f55 100644 --- a/web/match_play_test.go +++ b/web/match_play_test.go @@ -227,12 +227,12 @@ func TestMatchPlayWebsocketCommands(t *testing.T) { // Test match setup commands. ws.Write("substituteTeam", nil) assert.Contains(t, readWebsocketError(t, ws), "Invalid alliance station") - ws.Write("substituteTeam", map[string]interface{}{"team": 254, "position": "B5"}) + ws.Write("substituteTeam", map[string]any{"team": 254, "position": "B5"}) assert.Contains(t, readWebsocketError(t, ws), "Invalid alliance station") - ws.Write("substituteTeam", map[string]interface{}{"team": 254, "position": "B1"}) + ws.Write("substituteTeam", map[string]any{"team": 254, "position": "B1"}) readWebsocketType(t, ws, "arenaStatus") assert.Equal(t, 254, web.arena.CurrentMatch.Blue1) - ws.Write("substituteTeam", map[string]interface{}{"team": 0, "position": "B1"}) + ws.Write("substituteTeam", map[string]any{"team": 0, "position": "B1"}) readWebsocketType(t, ws, "arenaStatus") assert.Equal(t, 0, web.arena.CurrentMatch.Blue1) ws.Write("toggleBypass", nil) @@ -270,7 +270,7 @@ func TestMatchPlayWebsocketCommands(t *testing.T) { readWebsocketType(t, ws, "audienceDisplayMode") readWebsocketType(t, ws, "allianceStationDisplayMode") assert.Equal(t, field.PostMatch, web.arena.MatchState) - ws.Write("updateRealtimeScore", map[string]interface{}{ + ws.Write("updateRealtimeScore", map[string]any{ "blueAuto": 10, "redAuto": 20, "blueTeleop": 30, @@ -379,7 +379,7 @@ func readWebsocketStatusMatchTime(t *testing.T, ws *websocket.Websocket) (bool, return getStatusMatchTime(t, readWebsocketMultiple(t, ws, 2)) } -func getStatusMatchTime(t *testing.T, messages map[string]interface{}) (bool, field.MatchTimeMessage) { +func getStatusMatchTime(t *testing.T, messages map[string]any) (bool, field.MatchTimeMessage) { _, statusReceived := messages["arenaStatus"] message, ok := messages["matchTime"] var matchTime field.MatchTimeMessage diff --git a/web/setup_lower_thirds_test.go b/web/setup_lower_thirds_test.go index ea2d497b..dcae8133 100644 --- a/web/setup_lower_thirds_test.go +++ b/web/setup_lower_thirds_test.go @@ -54,7 +54,7 @@ func TestSetupLowerThirds(t *testing.T) { assert.Equal(t, "Top Text 6", lowerThird.TopText) assert.Equal(t, false, web.arena.ShowLowerThird) - ws.Write("reorderLowerThird", map[string]interface{}{"Id": 2, "moveUp": false}) + ws.Write("reorderLowerThird", map[string]any{"Id": 2, "moveUp": false}) time.Sleep(time.Millisecond * 100) lowerThirds, _ := web.arena.Database.GetAllLowerThirds() assert.Equal(t, 3, lowerThirds[0].Id) diff --git a/web/web.go b/web/web.go index da17672f..995d902d 100755 --- a/web/web.go +++ b/web/web.go @@ -33,11 +33,11 @@ func NewWeb(arena *field.Arena) *Web { // Helper functions that can be used inside templates. web.templateHelpers = template.FuncMap{ // Allows sub-templates to be invoked with multiple arguments. - "dict": func(values ...interface{}) (map[string]interface{}, error) { + "dict": func(values ...any) (map[string]any, error) { if len(values)%2 != 0 { return nil, fmt.Errorf("Invalid dict call.") } - dict := make(map[string]interface{}, len(values)/2) + dict := make(map[string]any, len(values)/2) for i := 0; i < len(values); i += 2 { key, ok := values[i].(string) if !ok { diff --git a/web/web_test.go b/web/web_test.go index 538853b4..e488df2b 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -80,7 +80,7 @@ func readWebsocketError(t *testing.T, ws *websocket.Websocket) string { } // Receives the next websocket message and asserts that it is of the given type. -func readWebsocketType(t *testing.T, ws *websocket.Websocket, expectedMessageType string) interface{} { +func readWebsocketType(t *testing.T, ws *websocket.Websocket, expectedMessageType string) any { messageType, message, err := ws.ReadWithTimeout(time.Second) if assert.Nil(t, err) { assert.Equal(t, expectedMessageType, messageType) @@ -88,8 +88,8 @@ func readWebsocketType(t *testing.T, ws *websocket.Websocket, expectedMessageTyp return message } -func readWebsocketMultiple(t *testing.T, ws *websocket.Websocket, count int) map[string]interface{} { - messages := make(map[string]interface{}) +func readWebsocketMultiple(t *testing.T, ws *websocket.Websocket, count int) map[string]any { + messages := make(map[string]any) for i := 0; i < count; i++ { messageType, message, err := ws.ReadWithTimeout(time.Second) if assert.Nil(t, err) { diff --git a/websocket/notifier.go b/websocket/notifier.go index 9563d717..0b45d55b 100644 --- a/websocket/notifier.go +++ b/websocket/notifier.go @@ -15,17 +15,17 @@ const notifyBufferSize = 5 type Notifier struct { messageType string - messageProducer func() interface{} + messageProducer func() any listeners map[chan messageEnvelope]struct{} // The map is essentially a set; the value is ignored. mutex sync.Mutex } type messageEnvelope struct { messageType string - messageBody interface{} + messageBody any } -func NewNotifier(messageType string, messageProducer func() interface{}) *Notifier { +func NewNotifier(messageType string, messageProducer func() any) *Notifier { notifier := &Notifier{messageType: messageType, messageProducer: messageProducer} notifier.listeners = make(map[chan messageEnvelope]struct{}) return notifier @@ -39,7 +39,7 @@ func (notifier *Notifier) Notify() { // Sends the given message to all registered listeners, and cleans up any listeners that have closed. If there is a // messageProducer function defined it is ignored. -func (notifier *Notifier) NotifyWithMessage(messageBody interface{}) { +func (notifier *Notifier) NotifyWithMessage(messageBody any) { notifier.mutex.Lock() defer notifier.mutex.Unlock() @@ -79,7 +79,7 @@ func (notifier *Notifier) listen() chan messageEnvelope { } // Invokes the message producer to get the message, or returns nil if no producer is defined. -func (notifier *Notifier) getMessageBody() interface{} { +func (notifier *Notifier) getMessageBody() any { if notifier.messageProducer == nil { return nil } else { diff --git a/websocket/notifier_test.go b/websocket/notifier_test.go index 1b1567cd..edaf2975 100644 --- a/websocket/notifier_test.go +++ b/websocket/notifier_test.go @@ -40,7 +40,7 @@ func TestNotifier(t *testing.T) { notifier.NotifyWithMessage(i) } var value messageEnvelope - var lastValue interface{} + var lastValue any for lastValue == nil { select { case value = <-listener: @@ -85,6 +85,6 @@ func TestNotifyMultipleListeners(t *testing.T) { } } -func generateTestMessage() interface{} { +func generateTestMessage() any { return "test message" } diff --git a/websocket/websocket.go b/websocket/websocket.go index c4483e5a..54204ed3 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -28,8 +28,8 @@ type Websocket struct { } type Message struct { - Type string `json:"type"` - Data interface{} `json:"data"` + Type string `json:"type"` + Data any `json:"data"` } var websocketUpgrader = websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 2014} @@ -64,7 +64,7 @@ func (ws *Websocket) PongHandler(string) error { return nil } -func (ws *Websocket) Read() (string, interface{}, error) { +func (ws *Websocket) Read() (string, any, error) { var message Message err := ws.conn.ReadJSON(&message) if websocket.IsCloseError(err, websocket.CloseNoStatusReceived, websocket.CloseAbnormalClosure) { @@ -96,10 +96,10 @@ func (ws *Websocket) SendPing() error { return nil } -func (ws *Websocket) ReadWithTimeout(timeout time.Duration) (string, interface{}, error) { +func (ws *Websocket) ReadWithTimeout(timeout time.Duration) (string, any, error) { type wsReadResult struct { messageType string - message interface{} + message any err error } readChan := make(chan wsReadResult, 1) @@ -116,7 +116,7 @@ func (ws *Websocket) ReadWithTimeout(timeout time.Duration) (string, interface{} } } -func (ws *Websocket) Write(messageType string, data interface{}) error { +func (ws *Websocket) Write(messageType string, data any) error { ws.writeMutex.Lock() defer ws.writeMutex.Unlock() err := ws.conn.WriteJSON(Message{messageType, data}) diff --git a/websocket/websocket_test.go b/websocket/websocket_test.go index 9e10af51..d9ed01d0 100644 --- a/websocket/websocket_test.go +++ b/websocket/websocket_test.go @@ -15,10 +15,10 @@ import ( func TestWebsocket(t *testing.T) { // Set up some fake notifiers. - notifier1 := NewNotifier("messageType1", func() interface{} { return "test message" }) + notifier1 := NewNotifier("messageType1", func() any { return "test message" }) notifier2 := NewNotifier("messageType2", nil) changingValue := 123.45 - notifier3 := NewNotifier("messageType3", func() interface{} { return changingValue }) + notifier3 := NewNotifier("messageType3", func() any { return changingValue }) // Start up a fake server with a trivial websocket handler. testWebsocketHandler := func(w http.ResponseWriter, r *http.Request) { @@ -107,7 +107,7 @@ func TestWebsocket(t *testing.T) { assert.Equal(t, 0, len(notifier1.listeners)) } -func assertMessage(t *testing.T, ws *Websocket, expectedMessageType string, expectedMessageBody interface{}) { +func assertMessage(t *testing.T, ws *Websocket, expectedMessageType string, expectedMessageBody any) { messageType, messageBody, err := ws.ReadWithTimeout(time.Second) if assert.Nil(t, err) { assert.Equal(t, expectedMessageType, messageType) From ff55c2d1e2b5ea78f04fb6e53865296bdab95f25 Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:52:03 -0500 Subject: [PATCH 03/40] Add footer containing time generated to PDF reports. Changes from upstream --- web/reports.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/web/reports.go b/web/reports.go index e581b6a1..a0ce8e47 100755 --- a/web/reports.go +++ b/web/reports.go @@ -17,6 +17,7 @@ import ( "github.com/jung-kurt/gofpdf" "net/http" "strconv" + "time" ) // Generates a CSV-formatted report of the qualification rankings. @@ -84,6 +85,8 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request) pdf.CellFormat(colWidths["Played"], rowHeight, strconv.Itoa(ranking.Played), "1", 1, "C", false, 0, "") } + addTimeGeneratedFooter(pdf) + // Write out the PDF file as the HTTP response. w.Header().Set("Content-Type", "application/pdf") err = pdf.Output(w) @@ -228,6 +231,8 @@ func (web *Web) backupsPdfReportHandler(w http.ResponseWriter, r *http.Request) pdf.CellFormat(colWidths["RP"], rowHeight, strconv.Itoa(ranking.RankingPoints), "1", 1, "C", false, 0, "") } + addTimeGeneratedFooter(pdf) + // Write out the PDF file as the HTTP response. w.Header().Set("Content-Type", "application/pdf") err = pdf.Output(w) @@ -287,6 +292,8 @@ func (web *Web) couponsPdfReportHandler(w http.ResponseWriter, r *http.Request) } } + addTimeGeneratedFooter(pdf) + // Write out the PDF file as the HTTP response. w.Header().Set("Content-Type", "application/pdf") err = pdf.Output(w) @@ -474,6 +481,8 @@ func (web *Web) schedulePdfReportHandler(w http.ResponseWriter, r *http.Request) pdf.CellFormat(195, 10, fmt.Sprintf("Matches Per Team: %d", matchesPerTeam), "", 1, "L", false, 0, "") } + addTimeGeneratedFooter(pdf) + // Write out the PDF file as the HTTP response. w.Header().Set("Content-Type", "application/pdf") err = pdf.Output(w) @@ -559,6 +568,8 @@ func (web *Web) teamsPdfReportHandler(w http.ResponseWriter, r *http.Request) { } } + addTimeGeneratedFooter(pdf) + // Write out the PDF file as the HTTP response. w.Header().Set("Content-Type", "application/pdf") err = pdf.Output(w) @@ -668,6 +679,8 @@ func (web *Web) alliancesPdfReportHandler(w http.ResponseWriter, r *http.Request } } + addTimeGeneratedFooter(pdf) + // Write out the PDF file as the HTTP response. w.Header().Set("Content-Type", "application/pdf") err = pdf.Output(w) @@ -707,3 +720,11 @@ func surrogateText(isSurrogate bool) string { return "" } } + +func addTimeGeneratedFooter(pdf *gofpdf.Fpdf) { + footerText := fmt.Sprintf( + "Report generated at %s on %s", time.Now().Format("3:04:05 PM"), time.Now().Format("Mon Jan 2 2006"), + ) + pdf.SetFont("Arial", "", 10) + pdf.CellFormat(0, 10, footerText, "", 1, "L", false, 0, "") +} From e101cc9678a5b7e51d1fd6fab5d7975711180afb Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:53:31 -0500 Subject: [PATCH 04/40] Name WPA keys file with a static four-character name for compatibility with the kiosk Changes from upstream --- web/reports.go | 2 +- web/reports_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/reports.go b/web/reports.go index a0ce8e47..bc4eb3e9 100755 --- a/web/reports.go +++ b/web/reports.go @@ -592,7 +592,7 @@ func (web *Web) wpaKeysCsvReportHandler(w http.ResponseWriter, r *http.Request) } w.Header().Set("Content-Type", "text/csv") - w.Header().Set("Content-Disposition", "attachment; filename=wpa_keys.csv") + w.Header().Set("Content-Disposition", "attachment; filename=keys.csv") for _, team := range teams { _, err := w.Write([]byte(fmt.Sprintf("%d,%s\r\n", team.Id, team.WpaKey))) if err != nil { diff --git a/web/reports_test.go b/web/reports_test.go index 7524bfdc..6043a3ae 100644 --- a/web/reports_test.go +++ b/web/reports_test.go @@ -126,7 +126,7 @@ func TestWpaKeysCsvReport(t *testing.T) { recorder := web.getHttpResponse("/reports/csv/wpa_keys") assert.Equal(t, 200, recorder.Code) assert.Equal(t, "text/csv", recorder.Header()["Content-Type"][0]) - assert.Equal(t, "attachment; filename=wpa_keys.csv", recorder.Header()["Content-Disposition"][0]) + assert.Equal(t, "attachment; filename=keys.csv", recorder.Header()["Content-Disposition"][0]) assert.Equal(t, "254,12345678\r\n1114,9876543210\r\n", recorder.Body.String()) } From 9191e00570859699283b067e5ddb502c4059723c Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:56:40 -0500 Subject: [PATCH 05/40] Adjust alliance station display font sizes Changes from upstream --- static/css/alliance_station_display.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/css/alliance_station_display.css b/static/css/alliance_station_display.css index d16a0ac7..7eaabc03 100644 --- a/static/css/alliance_station_display.css +++ b/static/css/alliance_station_display.css @@ -118,6 +118,7 @@ body[data-position=left] #inMatch #redScore { } body[data-position=middle] #inMatch #timeRemaining { display: block; + font-size: 32vw; } body[data-position=right] #inMatch #blueScore { display: block; @@ -131,7 +132,7 @@ body[data-position=right] #inMatch #blueScore { left: 0; right: 0; margin: 0 auto; - font-size: 450px; + font-size: 35vw; line-height: 450px; text-align: center; } From eb4c2e9c26882679b1823a9a700a856342a4d02d Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 08:03:21 -0500 Subject: [PATCH 06/40] Get rid of admin Wi-Fi configuration (since it is strongly recommended to use a standalone AP to avoid disconnections). Changes from upstream --- field/arena.go | 27 ++++++++++++++------------- model/event_settings.go | 4 ---- model/event_settings_test.go | 2 -- network/access_point.go | 31 ++----------------------------- templates/setup_settings.html | 17 ----------------- web/setup_settings.go | 2 -- 6 files changed, 16 insertions(+), 67 deletions(-) diff --git a/field/arena.go b/field/arena.go index cac081d2..48fc247d 100755 --- a/field/arena.go +++ b/field/arena.go @@ -148,24 +148,25 @@ func (arena *Arena) LoadSettings() error { arena.EventSettings = settings // Initialize the components that depend on settings. - arena.accessPoint.SetSettings(settings.ApAddress, settings.ApUsername, settings.ApPassword, - settings.ApTeamChannel, settings.ApAdminChannel, settings.ApAdminWpaKey, settings.NetworkSecurityEnabled) - arena.accessPoint2.SetSettings(settings.Ap2Address, settings.Ap2Username, settings.Ap2Password, - settings.Ap2TeamChannel, 0, "", settings.NetworkSecurityEnabled) + arena.accessPoint.SetSettings( + settings.ApAddress, + settings.ApUsername, + settings.ApPassword, + settings.ApTeamChannel, + settings.NetworkSecurityEnabled, + ) + arena.accessPoint2.SetSettings( + settings.Ap2Address, + settings.Ap2Username, + settings.Ap2Password, + settings.Ap2TeamChannel, + settings.NetworkSecurityEnabled, + ) arena.networkSwitch = network.NewSwitch(settings.SwitchAddress, settings.SwitchPassword) arena.dnsMasq = network.NewDnsMasq() arena.Plc.SetAddress(settings.PlcAddress) arena.TbaClient = partner.NewTbaClient(settings.TbaEventCode, settings.TbaSecretId, settings.TbaSecret) - if arena.EventSettings.NetworkSecurityEnabled && arena.MatchState == PreMatch { - if err = arena.accessPoint.ConfigureAdminWifi(); err != nil { - log.Printf("Failed to configure admin WiFi: %s", err.Error()) - } - if err = arena.accessPoint2.ConfigureAdminWifi(); err != nil { - log.Printf("Failed to configure admin WiFi: %s", err.Error()) - } - } - game.MatchTiming.WarmupDurationSec = settings.WarmupDurationSec game.MatchTiming.AutoDurationSec = settings.AutoDurationSec game.MatchTiming.PauseDurationSec = settings.PauseDurationSec diff --git a/model/event_settings.go b/model/event_settings.go index bad0e725..7c343568 100755 --- a/model/event_settings.go +++ b/model/event_settings.go @@ -24,8 +24,6 @@ type EventSettings struct { ApUsername string ApPassword string ApTeamChannel int - ApAdminChannel int - ApAdminWpaKey string Ap2Address string Ap2Username string Ap2Password string @@ -59,8 +57,6 @@ func (database *Database) GetEventSettings() (*EventSettings, error) { SelectionRound3Order: "", TBADownloadEnabled: true, ApTeamChannel: 157, - ApAdminChannel: 0, - ApAdminWpaKey: "1234Five", Ap2TeamChannel: 0, WarmupDurationSec: game.MatchTiming.WarmupDurationSec, AutoDurationSec: game.MatchTiming.AutoDurationSec, diff --git a/model/event_settings_test.go b/model/event_settings_test.go index b16591e9..83f73dea 100644 --- a/model/event_settings_test.go +++ b/model/event_settings_test.go @@ -25,8 +25,6 @@ func TestEventSettingsReadWrite(t *testing.T) { SelectionRound3Order: "", TBADownloadEnabled: true, ApTeamChannel: 157, - ApAdminChannel: 0, - ApAdminWpaKey: "1234Five", WarmupDurationSec: 0, AutoDurationSec: 15, PauseDurationSec: 3, diff --git a/network/access_point.go b/network/access_point.go index d159402e..5f00c9e9 100644 --- a/network/access_point.go +++ b/network/access_point.go @@ -30,8 +30,6 @@ type AccessPoint struct { username string password string teamChannel int - adminChannel int - adminWpaKey string networkSecurityEnabled bool configRequestChan chan [6]*model.Team TeamWifiStatuses [6]TeamWifiStatus @@ -48,14 +46,11 @@ type sshOutput struct { err error } -func (ap *AccessPoint) SetSettings(address, username, password string, teamChannel, adminChannel int, - adminWpaKey string, networkSecurityEnabled bool) { +func (ap *AccessPoint) SetSettings(address, username, password string, teamChannel int, networkSecurityEnabled bool) { ap.address = address ap.username = username ap.password = password ap.teamChannel = teamChannel - ap.adminChannel = adminChannel - ap.adminWpaKey = adminWpaKey ap.networkSecurityEnabled = networkSecurityEnabled // Create config channel the first time this method is called. @@ -93,27 +88,6 @@ func (ap *AccessPoint) ConfigureTeamWifi(teams [6]*model.Team) error { } } -func (ap *AccessPoint) ConfigureAdminWifi() error { - if !ap.networkSecurityEnabled { - return nil - } - - disabled := 0 - if ap.adminChannel == 0 { - disabled = 1 - } - commands := []string{ - fmt.Sprintf("set wireless.radio0.channel='%d'", ap.teamChannel), - fmt.Sprintf("set wireless.radio1.disabled='%d'", disabled), - fmt.Sprintf("set wireless.radio1.channel='%d'", ap.adminChannel), - fmt.Sprintf("set wireless.@wifi-iface[0].key='%s'", ap.adminWpaKey), - "commit wireless", - } - command := fmt.Sprintf("uci batch < -
- -
- -
-
-
- -
- -
-
diff --git a/web/setup_settings.go b/web/setup_settings.go index b3840491..bd701177 100755 --- a/web/setup_settings.go +++ b/web/setup_settings.go @@ -66,8 +66,6 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) { eventSettings.ApUsername = r.PostFormValue("apUsername") eventSettings.ApPassword = r.PostFormValue("apPassword") eventSettings.ApTeamChannel, _ = strconv.Atoi(r.PostFormValue("apTeamChannel")) - eventSettings.ApAdminChannel, _ = strconv.Atoi(r.PostFormValue("apAdminChannel")) - eventSettings.ApAdminWpaKey = r.PostFormValue("apAdminWpaKey") eventSettings.Ap2Address = r.PostFormValue("ap2Address") eventSettings.Ap2Username = r.PostFormValue("ap2Username") eventSettings.Ap2Password = r.PostFormValue("ap2Password") From c9cdceb9f1becef5743e7c8a248db5527bdf7ce6 Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 08:18:58 -0500 Subject: [PATCH 07/40] Update stack light status to show disconnected robots during a match. --- field/arena.go | 40 ++++++++++++++++++++++------------------ field/arena_test.go | 38 +++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/field/arena.go b/field/arena.go index 48fc247d..8122dc9d 100755 --- a/field/arena.go +++ b/field/arena.go @@ -195,7 +195,7 @@ func (arena *Arena) CreatePlayoffBracket() error { case "double": arena.PlayoffBracket, err = bracket.NewDoubleEliminationBracket(arena.EventSettings.NumElimAlliances) default: - err = fmt.Errorf("Invalid playoff type: %v", arena.EventSettings.ElimType) + err = fmt.Errorf("invalid playoff type: %v", arena.EventSettings.ElimType) } return err } @@ -216,7 +216,7 @@ func (arena *Arena) UpdatePlayoffBracket(startTime *time.Time) error { // Sets up the arena for the given match. func (arena *Arena) LoadMatch(match *model.Match) error { if arena.MatchState != PreMatch { - return fmt.Errorf("Cannot load match while there is a match still in progress or with results pending.") + return fmt.Errorf("cannot load match while there is a match still in progress or with results pending") } arena.CurrentMatch = match @@ -286,7 +286,7 @@ func (arena *Arena) LoadNextMatch() error { // Assigns the given team to the given station, also substituting it into the match record. func (arena *Arena) SubstituteTeam(teamId int, station string) error { if !arena.CurrentMatch.ShouldAllowSubstitution() { - return fmt.Errorf("Can't substitute teams for qualification matches.") + return fmt.Errorf("can't substitute teams for qualification matches") } err := arena.assignTeam(teamId, station) if err != nil { @@ -353,7 +353,7 @@ func (arena *Arena) StartMatch() error { // Kills the current match or timeout if it is underway. func (arena *Arena) AbortMatch() error { if arena.MatchState == PreMatch || arena.MatchState == PostMatch || arena.MatchState == PostTimeout { - return fmt.Errorf("Cannot abort match when it is not in progress.") + return fmt.Errorf("cannot abort match when it is not in progress") } if arena.MatchState == TimeoutActive { @@ -377,7 +377,7 @@ func (arena *Arena) AbortMatch() error { // Clears out the match and resets the arena state unless there is a match underway. func (arena *Arena) ResetMatch() error { if arena.MatchState != PostMatch && arena.MatchState != PreMatch { - return fmt.Errorf("Cannot reset match while it is in progress.") + return fmt.Errorf("cannot reset match while it is in progress") } arena.MatchState = PreMatch arena.matchAborted = false @@ -394,7 +394,7 @@ func (arena *Arena) ResetMatch() error { // Starts a timeout of the given duration. func (arena *Arena) StartTimeout(durationSec int) error { if arena.MatchState != PreMatch { - return fmt.Errorf("Cannot start timeout while there is a match still in progress or with results pending.") + return fmt.Errorf("cannot start timeout while there is a match still in progress or with results pending") } game.MatchTiming.TimeoutDurationSec = durationSec @@ -600,7 +600,7 @@ func (arena *Arena) BlueScoreSummary() *game.ScoreSummary { func (arena *Arena) assignTeam(teamId int, station string) error { // Reject invalid station values. if _, ok := arena.AllianceStations[station]; !ok { - return fmt.Errorf("Invalid alliance station '%s'.", station) + return fmt.Errorf("invalid alliance station '%s'", station) } // Do nothing if the station is already assigned to the requested team. @@ -714,7 +714,7 @@ func (arena *Arena) setupNetwork(teams [6]*model.Team) { // Returns nil if the match can be started, and an error otherwise. func (arena *Arena) checkCanStartMatch() error { if arena.MatchState != PreMatch { - return fmt.Errorf("Cannot start match while there is a match still in progress or with results pending.") + return fmt.Errorf("cannot start match while there is a match still in progress or with results pending") } err := arena.checkAllianceStationsReady("R1", "R2", "R3", "B1", "B2", "B3") @@ -729,14 +729,14 @@ func (arena *Arena) checkCanStartMatch() error { if arena.Plc.IsEnabled() { if !arena.Plc.IsHealthy { - return fmt.Errorf("Cannot start match while PLC is not healthy.") + return fmt.Errorf("cannot start match while PLC is not healthy") } if arena.Plc.GetFieldEstop() { - return fmt.Errorf("Cannot start match while field emergency stop is active.") + return fmt.Errorf("cannot start match while field emergency stop is active") } for name, status := range arena.Plc.GetArmorBlockStatuses() { if !status { - return fmt.Errorf("Cannot start match while PLC ArmorBlock '%s' is not connected.", name) + return fmt.Errorf("cannot start match while PLC ArmorBlock '%s' is not connected", name) } } } @@ -748,19 +748,19 @@ func (arena *Arena) checkAllianceStationsReady(stations ...string) error { for _, station := range stations { allianceStation := arena.AllianceStations[station] if allianceStation.Estop { - return fmt.Errorf("Cannot start match while an emergency stop is active.") + return fmt.Errorf("cannot start match while an emergency stop is active") } if !allianceStation.Bypass { if allianceStation.DsConn == nil || !allianceStation.DsConn.RobotLinked { - return fmt.Errorf("Cannot start match until all robots are connected or bypassed.") + return fmt.Errorf("cannot start match until all robots are connected or bypassed") } if station[0] == 'R' { if !arena.Scc.IsSccConnected("red") { - return fmt.Errorf("Cannot start match without red alliance SCC connected") + return fmt.Errorf("cannot start match without red alliance SCC connected") } } else if station[0] == 'B' { if !arena.Scc.IsSccConnected("blue") { - return fmt.Errorf("Cannot start match without blue alliance SCC connected") + return fmt.Errorf("cannot start match without blue alliance SCC connected") } } } @@ -773,7 +773,7 @@ func (arena *Arena) checkSccEstops() error { for alliance, status := range arena.Scc.status { for i := range status.EStops { if status.EStops[i] { - return fmt.Errorf("Cannot start match with %s %d emergency stop active", + return fmt.Errorf("cannot start match with %s %d emergency stop active", alliance, i+1) } } @@ -840,6 +840,9 @@ func (arena *Arena) handlePlcInput() { // Updates the PLC's coils based on its inputs and the current scoring state. func (arena *Arena) handlePlcOutput() { + redAllianceReady := arena.checkAllianceStationsReady("R1", "R2", "R3") == nil + blueAllianceReady := arena.checkAllianceStationsReady("B1", "B2", "B3") == nil + switch arena.MatchState { case PreMatch: if arena.lastMatchState != PreMatch { @@ -851,8 +854,6 @@ func (arena *Arena) handlePlcOutput() { case PostTimeout: // Set the stack light state -- solid alliance color(s) if robots are not connected, solid orange if scores are // not input, or blinking green if ready. - redAllianceReady := arena.checkAllianceStationsReady("R1", "R2", "R3") == nil - blueAllianceReady := arena.checkAllianceStationsReady("B1", "B2", "B3") == nil greenStackLight := redAllianceReady && blueAllianceReady && arena.Plc.GetCycleState(2, 0, 2) arena.Plc.SetStackLights(!redAllianceReady, !blueAllianceReady, false, greenStackLight) arena.Plc.SetStackBuzzer(redAllianceReady && blueAllianceReady) @@ -877,10 +878,13 @@ func (arena *Arena) handlePlcOutput() { }() } case AutoPeriod: + arena.Plc.SetStackBuzzer(false) + arena.Plc.SetStackLights(!redAllianceReady, !blueAllianceReady, false, true) fallthrough case PausePeriod: fallthrough case TeleopPeriod: + arena.Plc.SetStackLights(!redAllianceReady, !blueAllianceReady, false, true) } } diff --git a/field/arena_test.go b/field/arena_test.go index 52cead82..118671c7 100644 --- a/field/arena_test.go +++ b/field/arena_test.go @@ -59,7 +59,7 @@ func TestArenaCheckCanStartMatch(t *testing.T) { // Check robot state constraints. err := arena.checkCanStartMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot start match until all robots are connected or bypassed") + assert.Contains(t, err.Error(), "cannot start match until all robots are connected or bypassed") } arena.AllianceStations["R1"].Bypass = true arena.AllianceStations["R2"].Bypass = true @@ -68,7 +68,7 @@ func TestArenaCheckCanStartMatch(t *testing.T) { arena.AllianceStations["B2"].Bypass = true err = arena.checkCanStartMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot start match until all robots are connected or bypassed") + assert.Contains(t, err.Error(), "cannot start match until all robots are connected or bypassed") } arena.AllianceStations["B3"].Bypass = true assert.Nil(t, arena.checkCanStartMatch()) @@ -77,7 +77,7 @@ func TestArenaCheckCanStartMatch(t *testing.T) { arena.Plc.SetAddress("1.2.3.4") err = arena.checkCanStartMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot start match while PLC is not healthy") + assert.Contains(t, err.Error(), "cannot start match while PLC is not healthy") } arena.Plc.SetAddress("") assert.Nil(t, arena.checkCanStartMatch()) @@ -216,73 +216,73 @@ func TestArenaStateEnforcement(t *testing.T) { assert.Nil(t, err) err = arena.AbortMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot abort match when") + assert.Contains(t, err.Error(), "cannot abort match when") } err = arena.StartMatch() assert.Nil(t, err) err = arena.LoadMatch(new(model.Match)) if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot load match while") + assert.Contains(t, err.Error(), "cannot load match while") } err = arena.StartMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot start match while") + assert.Contains(t, err.Error(), "cannot start match while") } err = arena.ResetMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot reset match while") + assert.Contains(t, err.Error(), "cannot reset match while") } arena.MatchState = AutoPeriod err = arena.LoadMatch(new(model.Match)) if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot load match while") + assert.Contains(t, err.Error(), "cannot load match while") } err = arena.StartMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot start match while") + assert.Contains(t, err.Error(), "cannot start match while") } err = arena.ResetMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot reset match while") + assert.Contains(t, err.Error(), "cannot reset match while") } arena.MatchState = PausePeriod err = arena.LoadMatch(new(model.Match)) if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot load match while") + assert.Contains(t, err.Error(), "cannot load match while") } err = arena.StartMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot start match while") + assert.Contains(t, err.Error(), "cannot start match while") } err = arena.ResetMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot reset match while") + assert.Contains(t, err.Error(), "cannot reset match while") } arena.MatchState = TeleopPeriod err = arena.LoadMatch(new(model.Match)) if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot load match while") + assert.Contains(t, err.Error(), "cannot load match while") } err = arena.StartMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot start match while") + assert.Contains(t, err.Error(), "cannot start match while") } err = arena.ResetMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot reset match while") + assert.Contains(t, err.Error(), "cannot reset match while") } arena.MatchState = PostMatch err = arena.LoadMatch(new(model.Match)) if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot load match while") + assert.Contains(t, err.Error(), "cannot load match while") } err = arena.StartMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot start match while") + assert.Contains(t, err.Error(), "cannot start match while") } err = arena.AbortMatch() if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Cannot abort match when") + assert.Contains(t, err.Error(), "cannot abort match when") } err = arena.ResetMatch() From 77cdbc786c831cc2da4553b5d5630e8f1562b477 Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 08:21:59 -0500 Subject: [PATCH 08/40] Get rid of unused 'Signal Volunteers' button. --- field/arena.go | 2 -- static/js/match_play.js | 10 ---------- templates/match_play.html | 4 ---- web/match_play.go | 7 ------- 4 files changed, 23 deletions(-) diff --git a/field/arena.go b/field/arena.go index 8122dc9d..d15c659b 100755 --- a/field/arena.go +++ b/field/arena.go @@ -68,7 +68,6 @@ type Arena struct { lastDsPacketTime time.Time lastPeriodicTaskTime time.Time EventStatus EventStatus - FieldVolunteers bool FieldReset bool AudienceDisplayMode string SavedMatch *model.Match @@ -253,7 +252,6 @@ func (arena *Arena) LoadMatch(match *model.Match) error { arena.soundsPlayed = make(map[*game.MatchSound]struct{}) arena.RedScore = new(game.Score) arena.BlueScore = new(game.Score) - arena.FieldVolunteers = false arena.FieldReset = false arena.Plc.ResetMatch() diff --git a/static/js/match_play.js b/static/js/match_play.js index 3e77c4e1..79dfd370 100755 --- a/static/js/match_play.js +++ b/static/js/match_play.js @@ -28,11 +28,6 @@ var abortMatch = function() { websocket.send("abortMatch"); }; -// Sends a websocket message to signal to the volunteers that they may enter the field. -var signalVolunteers = function() { - websocket.send("signalVolunteers"); -}; - // Sends a websocket message to signal to the teams that they may enter the field. var signalReset = function() { websocket.send("signalReset"); @@ -179,7 +174,6 @@ var handleArenaStatus = function(data) { $("#matchStartReason").html(data.CanStartMatchReason); } $("#abortMatch").prop("disabled", true); - $("#signalVolunteers").prop("disabled", false); $("#signalReset").prop("disabled", false); $("#commitResults").prop("disabled", true); $("#discardResults").prop("disabled", true); @@ -205,7 +199,6 @@ var handleArenaStatus = function(data) { case "TELEOP_PERIOD": $("#startMatch").prop("disabled", true); $("#abortMatch").prop("disabled", false); - $("#signalVolunteers").prop("disabled", true); $("#signalReset").prop("disabled", true); $("#commitResults").prop("disabled", true); $("#discardResults").prop("disabled", true); @@ -221,7 +214,6 @@ var handleArenaStatus = function(data) { case "POST_MATCH": $("#startMatch").prop("disabled", true); $("#abortMatch").prop("disabled", true); - $("#signalVolunteers").prop("disabled", false); $("#signalReset").prop("disabled", false); $("#commitResults").prop("disabled", false); $("#discardResults").prop("disabled", false); @@ -237,7 +229,6 @@ var handleArenaStatus = function(data) { case "TIMEOUT_ACTIVE": $("#startMatch").prop("disabled", true); $("#abortMatch").prop("disabled", false); - $("#signalVolunteers").prop("disabled", true); $("#signalReset").prop("disabled", true); $("#commitResults").prop("disabled", true); $("#discardResults").prop("disabled", true); @@ -253,7 +244,6 @@ var handleArenaStatus = function(data) { case "POST_TIMEOUT": $("#startMatch").prop("disabled", true); $("#abortMatch").prop("disabled", true); - $("#signalVolunteers").prop("disabled", true); $("#signalReset").prop("disabled", true); $("#commitResults").prop("disabled", true); $("#discardResults").prop("disabled", true); diff --git a/templates/match_play.html b/templates/match_play.html index 76e26f0b..5cdf2e05 100755 --- a/templates/match_play.html +++ b/templates/match_play.html @@ -116,10 +116,6 @@ onclick="abortMatch();" disabled> Abort Match -
+ {{if .EventSettings.NetworkSecurityEnabled}} +
Network Status
+

+ Access Point
+ Switch +

+ {{end}} {{if .PlcIsEnabled}} -

PLC Status

+
PLC Status

-
- E-Stop + + E-Stop
{{range $name, $status := .PlcArmorBlockStatuses}} -
{{$name}} + {{$name}} {{end}}

{{end}} diff --git a/templates/setup_settings.html b/templates/setup_settings.html index a756210c..d3d7ea8e 100755 --- a/templates/setup_settings.html +++ b/templates/setup_settings.html @@ -154,7 +154,7 @@
- +
From 21e091f43e7a3a580d815dbb23657aa3753fabbe Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 19:28:47 -0500 Subject: [PATCH 39/40] Update DHCP Ranges Updated DHCP ranges to match FIRST FMS config https://docs.wpilib.org/en/stable/docs/networking/networking-introduction/networking-basics.html#ethernet-wireless --- network/switch.go | 3 ++- network/switch_test.go | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/network/switch.go b/network/switch.go index 1e239480..74d75b34 100644 --- a/network/switch.go +++ b/network/switch.go @@ -82,8 +82,9 @@ func (sw *Switch) ConfigureTeamEthernet(teams [6]*model.Team) error { "no access-list 1%d\n"+ "access-list 1%d permit ip 10.%s.0 0.0.0.255 host %s\n"+ "access-list 1%d permit udp any eq bootpc any eq bootps\n"+ + "access-list 1%d permit icmp any any\n"+ "interface Vlan%d\nip address 10.%s.%d 255.255.255.0\n", - vlan, vlan, teamPartialIp, ServerIpAddress, vlan, vlan, teamPartialIp, switchTeamGatewayAddress) + vlan, vlan, teamPartialIp, ServerIpAddress, vlan, vlan, vlan, teamPartialIp, switchTeamGatewayAddress) } addTeamVlan(teams[0], red1Vlan) addTeamVlan(teams[1], red2Vlan) diff --git a/network/switch_test.go b/network/switch_test.go index 7429ae09..360c9e39 100644 --- a/network/switch_test.go +++ b/network/switch_test.go @@ -48,6 +48,7 @@ func TestConfigureSwitch(t *testing.T) { "network 10.2.54.0 255.255.255.0\ndefault-router 10.2.54.4\nlease 7\n"+ "access-list 150 permit ip 10.2.54.0 0.0.0.255 host 10.0.100.5\n"+ "access-list 150 permit udp any eq bootpc any eq bootps\n"+ + "access-list 150 permit icmp any any\n"+ "interface Vlan50\nip address 10.2.54.4 255.255.255.0\n"+ "end\ncopy running-config startup-config\n\nexit\n", command2, @@ -68,31 +69,37 @@ func TestConfigureSwitch(t *testing.T) { "network 10.11.14.0 255.255.255.0\ndefault-router 10.11.14.4\nlease 7\n"+ "access-list 110 permit ip 10.11.14.0 0.0.0.255 host 10.0.100.5\n"+ "access-list 110 permit udp any eq bootpc any eq bootps\n"+ + "access-list 110 permit icmp any any\n"+ "interface Vlan10\nip address 10.11.14.4 255.255.255.0\n"+ "ip dhcp excluded-address 10.2.54.1 10.2.54.100\nip dhcp pool dhcp20\n"+ "network 10.2.54.0 255.255.255.0\ndefault-router 10.2.54.4\nlease 7\n"+ "access-list 120 permit ip 10.2.54.0 0.0.0.255 host 10.0.100.5\n"+ "access-list 120 permit udp any eq bootpc any eq bootps\n"+ + "access-list 120 permit icmp any any\n"+ "interface Vlan20\nip address 10.2.54.4 255.255.255.0\n"+ "ip dhcp excluded-address 10.2.96.1 10.2.96.100\nip dhcp pool dhcp30\n"+ "network 10.2.96.0 255.255.255.0\ndefault-router 10.2.96.4\nlease 7\n"+ "access-list 130 permit ip 10.2.96.0 0.0.0.255 host 10.0.100.5\n"+ "access-list 130 permit udp any eq bootpc any eq bootps\n"+ + "access-list 130 permit icmp any any\n"+ "interface Vlan30\nip address 10.2.96.4 255.255.255.0\n"+ "ip dhcp excluded-address 10.15.3.1 10.15.3.100\nip dhcp pool dhcp40\n"+ "network 10.15.3.0 255.255.255.0\ndefault-router 10.15.3.4\nlease 7\n"+ "access-list 140 permit ip 10.15.3.0 0.0.0.255 host 10.0.100.5\n"+ "access-list 140 permit udp any eq bootpc any eq bootps\n"+ + "access-list 140 permit icmp any any\n"+ "interface Vlan40\nip address 10.15.3.4 255.255.255.0\n"+ "ip dhcp excluded-address 10.16.78.1 10.16.78.100\nip dhcp pool dhcp50\n"+ "network 10.16.78.0 255.255.255.0\ndefault-router 10.16.78.4\nlease 7\n"+ "access-list 150 permit ip 10.16.78.0 0.0.0.255 host 10.0.100.5\n"+ "access-list 150 permit udp any eq bootpc any eq bootps\n"+ + "access-list 150 permit icmp any any\n"+ "interface Vlan50\nip address 10.16.78.4 255.255.255.0\n"+ "ip dhcp excluded-address 10.15.38.1 10.15.38.100\nip dhcp pool dhcp60\n"+ "network 10.15.38.0 255.255.255.0\ndefault-router 10.15.38.4\nlease 7\n"+ "access-list 160 permit ip 10.15.38.0 0.0.0.255 host 10.0.100.5\n"+ "access-list 160 permit udp any eq bootpc any eq bootps\n"+ + "access-list 160 permit icmp any any\n"+ "interface Vlan60\nip address 10.15.38.4 255.255.255.0\n"+ "end\ncopy running-config startup-config\n\nexit\n", command2, From 7aa774f30d554a95aa0f75aff9c2d03382200a6d Mon Sep 17 00:00:00 2001 From: Jay <44783760+jschenke488@users.noreply.github.com> Date: Tue, 29 Oct 2024 19:42:48 -0500 Subject: [PATCH 40/40] Update DHCP ranges and gateway IP --- network/dnsmasq.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/network/dnsmasq.go b/network/dnsmasq.go index 0b50d952..bd6d85b3 100644 --- a/network/dnsmasq.go +++ b/network/dnsmasq.go @@ -44,14 +44,15 @@ func (dm *DnsMasq) ConfigureTeamEthernet(teams [6]*model.Team) error { if oldTeamVlans[team.Id] == vlan { delete(oldTeamVlans, team.Id) } else { + teamPartialIp := fmt.Sprintf("%d.%d", team.Id/100, team.Id%100) contents := []byte(fmt.Sprintf( "# Options for VLAN%d\n"+ "# Team %d\n"+ "\n"+ - "dhcp-range=set:vlan%d,10.%d.%d.101,10.%d.%d.199,255.255.255.0,12h\n"+ - "dhcp-option=tag:vlan%d,3,10.%d.%d.61\n", - vlan, team.Id, vlan, team.Id/100, team.Id%100, team.Id/100, team.Id%100, - vlan, team.Id/100, team.Id%100)) + "dhcp-range=set:vlan%d,10.%s.20,10.%s.199,255.255.255.0,12h\n"+ + "dhcp-option=tag:vlan%d,3,10.%s.4\n", + vlan, team.Id, vlan, teamPartialIp, teamPartialIp, + vlan, teamPartialIp)) err := ioutil.WriteFile(fmt.Sprintf("/etc/dnsmasq.d/vlan%d.conf", vlan), contents, 0664) if err != nil { log.Printf("Failed to configure VLAN%d for team %d: %s", vlan, team.Id, err.Error())