diff --git a/field/arena.go b/field/arena.go index 1404f6c..9aae4a3 100644 --- a/field/arena.go +++ b/field/arena.go @@ -673,12 +673,16 @@ func (arena *Arena) handleLeds() { case TimeoutActive: fallthrough case PostTimeout: - // Set the stack light state -- blinking green if ready, or solid alliance color(s) if not. + // 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, greenStackLight) - arena.Plc.SetStackBuzzer(redAllianceReady && blueAllianceReady) + preMatchScoreReady := arena.BypassPreMatchScore || arena.RedRealtimeScore.CurrentScore.IsValidPreMatch() && + arena.BlueRealtimeScore.CurrentScore.IsValidPreMatch() + greenStackLight := redAllianceReady && blueAllianceReady && preMatchScoreReady && + arena.Plc.GetCycleState(2, 0, 2) + arena.Plc.SetStackLights(!redAllianceReady, !blueAllianceReady, !preMatchScoreReady, greenStackLight) + arena.Plc.SetStackBuzzer(redAllianceReady && blueAllianceReady && preMatchScoreReady) // Turn off lights if all teams become ready. // TODO(pat): Implement for 2019. @@ -700,9 +704,10 @@ func (arena *Arena) handleLeds() { //arena.BlueSwitchLeds.SetMode(led.BlueMode, led.BlueMode) } arena.lastBlueAllianceReady = blueAllianceReady - case PostMatch: - arena.Plc.SetStackLights(false, false, false) + scoreReady := arena.RedRealtimeScore.FoulsCommitted && arena.BlueRealtimeScore.FoulsCommitted && + arena.alliancePostMatchScoreReady("red") && arena.alliancePostMatchScoreReady("blue") + arena.Plc.SetStackLights(false, false, !scoreReady, false) } } @@ -745,3 +750,8 @@ func (arena *Arena) playSound(name string) { arena.PlaySoundNotifier.NotifyWithMessage(name) } } + +func (arena *Arena) alliancePostMatchScoreReady(alliance string) bool { + numPanels := arena.ScoringPanelRegistry.GetNumPanels(alliance) + return numPanels > 0 && arena.ScoringPanelRegistry.GetNumScoreCommitted(alliance) >= numPanels +} diff --git a/field/arena_notifiers.go b/field/arena_notifiers.go index 542cc56..073d21e 100644 --- a/field/arena_notifiers.go +++ b/field/arena_notifiers.go @@ -219,11 +219,14 @@ func (arena *Arena) generateScorePostedMessage() interface{} { func (arena *Arena) generateScoringStatusMessage() interface{} { return &struct { RefereeScoreReady bool + RedScoreReady bool + BlueScoreReady bool NumRedScoringPanels int NumRedScoringPanelsReady int NumBlueScoringPanels int NumBlueScoringPanelsReady int }{arena.RedRealtimeScore.FoulsCommitted && arena.BlueRealtimeScore.FoulsCommitted, + arena.alliancePostMatchScoreReady("red"), arena.alliancePostMatchScoreReady("blue"), arena.ScoringPanelRegistry.GetNumPanels("red"), arena.ScoringPanelRegistry.GetNumScoreCommitted("red"), arena.ScoringPanelRegistry.GetNumPanels("blue"), arena.ScoringPanelRegistry.GetNumScoreCommitted("blue")} } diff --git a/plc/plc.go b/plc/plc.go index cf3160c..76d9be5 100644 --- a/plc/plc.go +++ b/plc/plc.go @@ -102,29 +102,32 @@ func (plc *Plc) Run() { for { if plc.handler == nil { if plc.address == "" { - time.Sleep(time.Second * plcRetryIntevalSec) + // No PLC is configured; just allow the loop to continue to simulate inputs and outputs. plc.IsHealthy = false - continue - } - - err := plc.connect() - if err != nil { - log.Printf("PLC error: %v", err) - time.Sleep(time.Second * plcRetryIntevalSec) - plc.IsHealthy = false - continue + } else { + err := plc.connect() + if err != nil { + log.Printf("PLC error: %v", err) + //time.Sleep(time.Second * plcRetryIntevalSec) + plc.IsHealthy = false + continue + } } } startTime := time.Now() - isHealthy := true - isHealthy = isHealthy && plc.writeCoils() - isHealthy = isHealthy && plc.readInputs() - isHealthy = isHealthy && plc.readCounters() - if !isHealthy { - plc.resetConnection() + + if plc.handler != nil { + isHealthy := true + isHealthy = isHealthy && plc.writeCoils() + isHealthy = isHealthy && plc.readInputs() + isHealthy = isHealthy && plc.readCounters() + if !isHealthy { + plc.resetConnection() + } + plc.IsHealthy = isHealthy } - plc.IsHealthy = isHealthy + plc.cycleCounter++ if plc.cycleCounter == cycleCounterMax { plc.cycleCounter = 0 @@ -162,9 +165,10 @@ func (plc *Plc) GetTeamEstops() ([3]bool, [3]bool) { } // Set the on/off state of the stack lights on the scoring table. -func (plc *Plc) SetStackLights(red, blue, green bool) { +func (plc *Plc) SetStackLights(red, blue, orange, green bool) { plc.coils[stackLightRed] = red plc.coils[stackLightBlue] = blue + plc.coils[stackLightOrange] = orange plc.coils[stackLightGreen] = green } diff --git a/static/css/cheesy-arena.css b/static/css/cheesy-arena.css index 1875d68..e2971fc 100644 --- a/static/css/cheesy-arena.css +++ b/static/css/cheesy-arena.css @@ -92,6 +92,12 @@ width: 80px; } input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { - -webkit-appearance: none; - margin: 0; + -webkit-appearance: none; + margin: 0; +} +td[data-plc-value="false"] { + color: #f00; +} +td[data-plc-value="true"] { + color: #090; } diff --git a/static/js/match_play.js b/static/js/match_play.js index 3b4ed6e..5acfffe 100644 --- a/static/js/match_play.js +++ b/static/js/match_play.js @@ -222,14 +222,12 @@ var handleAudienceDisplayMode = function(data) { // Handles a websocket message to signal whether the referee and scorers have committed after the match. var handleScoringStatus = function(data) { - var redScoreReady = data.NumRedScoringPanels > 0 && data.NumRedScoringPanelsReady >= data.NumRedScoringPanels; - var blueScoreReady = data.NumBlueScoringPanels > 0 && data.NumBlueScoringPanelsReady >= data.NumBlueScoringPanels; - scoreIsReady = data.RefereeScoreReady && redScoreReady && blueScoreReady; + scoreIsReady = data.RefereeScoreReady && data.RedScoreReady && data.BlueScoreReady; $("#refereeScoreStatus").attr("data-ready", data.RefereeScoreReady); $("#redScoreStatus").text("Red Scoring " + data.NumRedScoringPanelsReady + "/" + data.NumRedScoringPanels); - $("#redScoreStatus").attr("data-ready", redScoreReady); + $("#redScoreStatus").attr("data-ready", data.RedScoreReady); $("#blueScoreStatus").text("Blue Scoring " + data.NumBlueScoringPanelsReady + "/" + data.NumBlueScoringPanels); - $("#blueScoreStatus").attr("data-ready", blueScoreReady); + $("#blueScoreStatus").attr("data-ready", data.BlueScoreReady); }; // Handles a websocket message to update the alliance station display screen selector. diff --git a/static/js/setup_led_plc.js b/static/js/setup_led_plc.js index 2d20279..f047d18 100644 --- a/static/js/setup_led_plc.js +++ b/static/js/setup_led_plc.js @@ -24,6 +24,7 @@ var handleLedMode = function(data) { var handlePlcIoChange = function(data) { $.each(data.Inputs, function(index, input) { $("#input" + index).text(input) + $("#input" + index).attr("data-plc-value", input); }); $.each(data.Registers, function(index, register) { @@ -32,6 +33,7 @@ var handlePlcIoChange = function(data) { $.each(data.Coils, function(index, coil) { $("#coil" + index).text(coil) + $("#coil" + index).attr("data-plc-value", coil); }); }; diff --git a/templates/setup_led_plc.html b/templates/setup_led_plc.html index 3a8da19..b87ca88 100644 --- a/templates/setup_led_plc.html +++ b/templates/setup_led_plc.html @@ -19,7 +19,7 @@ {{range $i, $name := .InputNames}}