mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
WIP: Light integration.
This commit is contained in:
15
arena.go
15
arena.go
@@ -89,6 +89,7 @@ type Arena struct {
|
||||
savedMatch *Match
|
||||
savedMatchResult *MatchResult
|
||||
leftGoalHotFirst bool
|
||||
lights Lights
|
||||
}
|
||||
|
||||
var mainArena Arena // Named thusly to avoid polluting the global namespace with something more generic.
|
||||
@@ -141,7 +142,7 @@ func (arena *Arena) Setup() {
|
||||
arena.allianceStationDisplays = make(map[string]string)
|
||||
arena.allianceStationDisplayScreen = "blank"
|
||||
|
||||
SetupLights()
|
||||
arena.lights.Setup()
|
||||
}
|
||||
|
||||
// Loads a team into an alliance station, cleaning up the previous team there if there is one.
|
||||
@@ -511,16 +512,16 @@ func (arena *Arena) handleLighting(alliance string, score *RealtimeScore) {
|
||||
switch arena.MatchState {
|
||||
case AUTO_PERIOD:
|
||||
leftSide := arena.MatchTimeSec() < float64(arena.matchTiming.AutoDurationSec)/2 == arena.leftGoalHotFirst
|
||||
SetHotGoalLights(alliance, leftSide)
|
||||
arena.lights.SetHotGoal(alliance, leftSide)
|
||||
case TELEOP_PERIOD:
|
||||
if score.AutoLeftoverBalls == 0 && score.CurrentCycle.Assists == 0 {
|
||||
SetPedestalLight(alliance)
|
||||
arena.lights.SetPedestal(alliance)
|
||||
} else {
|
||||
ClearPedestalLight(alliance)
|
||||
arena.lights.ClearPedestal(alliance)
|
||||
}
|
||||
SetAssistGoalLights(alliance, score.CurrentCycle.Assists)
|
||||
arena.lights.SetAssistGoal(alliance, score.CurrentCycle.Assists)
|
||||
default:
|
||||
ClearGoalLights(alliance)
|
||||
ClearPedestalLight(alliance)
|
||||
arena.lights.ClearGoal(alliance)
|
||||
arena.lights.ClearPedestal(alliance)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ CREATE TABLE event_settings (
|
||||
selectionround3order VARCHAR(1),
|
||||
teaminfodownloadenabled bool,
|
||||
alliancedisplayhotgoals bool,
|
||||
redgoallightsaddress VARCHAR(255),
|
||||
bluegoallightsaddress VARCHAR(255),
|
||||
tbapublishingenabled bool,
|
||||
tbaeventcode VARCHAR(16),
|
||||
tbasecretid VARCHAR(255),
|
||||
|
||||
@@ -15,6 +15,8 @@ type EventSettings struct {
|
||||
SelectionRound3Order string
|
||||
TeamInfoDownloadEnabled bool
|
||||
AllianceDisplayHotGoals bool
|
||||
RedGoalLightsAddress string
|
||||
BlueGoalLightsAddress string
|
||||
TbaPublishingEnabled bool
|
||||
TbaEventCode string
|
||||
TbaSecretId string
|
||||
|
||||
175
lights.go
175
lights.go
@@ -6,67 +6,154 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
var hotGoalLights map[string]bool
|
||||
var assistLights map[string]int
|
||||
var pedestalLights map[string]bool
|
||||
var newMatch bool
|
||||
type LightPacket [24]byte
|
||||
|
||||
func SetupLights() {
|
||||
hotGoalLights = make(map[string]bool)
|
||||
assistLights = make(map[string]int)
|
||||
pedestalLights = make(map[string]bool)
|
||||
newMatch = true
|
||||
type Lights struct {
|
||||
connections map[string]*net.Conn
|
||||
packets map[string]*LightPacket
|
||||
oldPackets map[string]*LightPacket
|
||||
hotGoal string
|
||||
newConnections bool
|
||||
}
|
||||
|
||||
func SetHotGoalLights(alliance string, leftSide bool) {
|
||||
if !newMatch && hotGoalLights[alliance] == leftSide {
|
||||
return
|
||||
func (lightPacket *LightPacket) setColor(channel int, color string) {
|
||||
switch color {
|
||||
case "red":
|
||||
lightPacket.setRgb(channel, 16, 0, 0)
|
||||
case "blue":
|
||||
lightPacket.setRgb(channel, 0, 0, 16)
|
||||
case "yellow":
|
||||
lightPacket.setRgb(channel, 16, 16, 0)
|
||||
default:
|
||||
lightPacket.setRgb(channel, 13, 12, 11)
|
||||
}
|
||||
newMatch = false
|
||||
hotGoalLights[alliance] = leftSide
|
||||
}
|
||||
|
||||
func (lightPacket *LightPacket) setRgb(channel int, red byte, green byte, blue byte) {
|
||||
lightPacket[channel*3] = red
|
||||
lightPacket[channel*3+1] = green
|
||||
lightPacket[channel*3+2] = blue
|
||||
}
|
||||
|
||||
func (lights *Lights) Setup() error {
|
||||
err := lights.SetupConnections()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lights.packets = make(map[string]*LightPacket)
|
||||
lights.packets["red"] = &LightPacket{}
|
||||
lights.packets["blue"] = &LightPacket{}
|
||||
lights.oldPackets = make(map[string]*LightPacket)
|
||||
lights.oldPackets["red"] = &LightPacket{}
|
||||
lights.oldPackets["blue"] = &LightPacket{}
|
||||
|
||||
lights.sendLights()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lights *Lights) SetupConnections() error {
|
||||
lights.connections = make(map[string]*net.Conn)
|
||||
|
||||
// Don't enable lights for a side if the controller address is not configured.
|
||||
if len(eventSettings.RedGoalLightsAddress) != 0 {
|
||||
conn, err := net.Dial("udp4", eventSettings.RedGoalLightsAddress)
|
||||
lights.connections["red"] = &conn
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(eventSettings.BlueGoalLightsAddress) != 0 {
|
||||
conn, err := net.Dial("udp4", eventSettings.BlueGoalLightsAddress)
|
||||
lights.connections["blue"] = &conn
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
lights.newConnections = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lights *Lights) SetHotGoal(alliance string, leftSide bool) {
|
||||
if leftSide {
|
||||
fmt.Printf("Setting left %s goal hot\n", alliance)
|
||||
mainArena.hotGoalLightNotifier.Notify("left")
|
||||
lights.packets[alliance].setRgb(0, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(1, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(2, 0, 0, 0)
|
||||
lights.packets[alliance].setColor(3, "yellow")
|
||||
lights.packets[alliance].setColor(4, "yellow")
|
||||
lights.packets[alliance].setColor(5, "yellow")
|
||||
lights.hotGoal = "left"
|
||||
} else {
|
||||
fmt.Printf("Setting right %s goal hot\n", alliance)
|
||||
mainArena.hotGoalLightNotifier.Notify("right")
|
||||
lights.packets[alliance].setColor(0, "yellow")
|
||||
lights.packets[alliance].setColor(1, "yellow")
|
||||
lights.packets[alliance].setColor(2, "yellow")
|
||||
lights.packets[alliance].setRgb(3, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(4, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(5, 0, 0, 0)
|
||||
lights.hotGoal = "right"
|
||||
}
|
||||
lights.sendLights()
|
||||
}
|
||||
|
||||
func SetAssistGoalLights(alliance string, numAssists int) {
|
||||
newMatch = true
|
||||
mainArena.hotGoalLightNotifier.Notify("")
|
||||
|
||||
if assistLights[alliance] == numAssists {
|
||||
return
|
||||
func (lights *Lights) SetAssistGoal(alliance string, numAssists int) {
|
||||
lights.packets[alliance].setRgb(0, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(1, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(2, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(3, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(4, 0, 0, 0)
|
||||
lights.packets[alliance].setRgb(5, 0, 0, 0)
|
||||
if numAssists > 0 {
|
||||
lights.packets[alliance].setColor(2, alliance)
|
||||
lights.packets[alliance].setColor(3, alliance)
|
||||
}
|
||||
assistLights[alliance] = numAssists
|
||||
if numAssists <= 0 {
|
||||
fmt.Printf("Clearing %s goal lights\n", alliance)
|
||||
} else if numAssists < 3 {
|
||||
fmt.Printf("Setting %s goal to %d assists\n", alliance, numAssists)
|
||||
if numAssists > 1 {
|
||||
lights.packets[alliance].setColor(1, alliance)
|
||||
lights.packets[alliance].setColor(4, alliance)
|
||||
}
|
||||
if numAssists > 2 {
|
||||
lights.packets[alliance].setColor(0, alliance)
|
||||
lights.packets[alliance].setColor(5, alliance)
|
||||
}
|
||||
lights.hotGoal = ""
|
||||
lights.sendLights()
|
||||
}
|
||||
|
||||
func (lights *Lights) ClearGoal(alliance string) {
|
||||
lights.SetAssistGoal(alliance, 0)
|
||||
}
|
||||
|
||||
func (lights *Lights) SetPedestal(alliance string) {
|
||||
if alliance == "red" {
|
||||
lights.packets["blue"].setColor(6, alliance)
|
||||
} else {
|
||||
fmt.Printf("Setting %s goal to 3 assists\n", alliance)
|
||||
lights.packets["red"].setColor(6, alliance)
|
||||
}
|
||||
lights.sendLights()
|
||||
}
|
||||
|
||||
func ClearGoalLights(alliance string) {
|
||||
SetAssistGoalLights(alliance, 0)
|
||||
func (lights *Lights) ClearPedestal(alliance string) {
|
||||
if alliance == "red" {
|
||||
lights.packets["blue"].setRgb(6, 0, 0, 0)
|
||||
} else {
|
||||
lights.packets["red"].setRgb(6, 0, 0, 0)
|
||||
}
|
||||
lights.sendLights()
|
||||
}
|
||||
|
||||
func SetPedestalLight(alliance string) {
|
||||
if pedestalLights[alliance] == false {
|
||||
pedestalLights[alliance] = true
|
||||
fmt.Printf("Setting %s pedestal\n", alliance)
|
||||
}
|
||||
}
|
||||
|
||||
func ClearPedestalLight(alliance string) {
|
||||
if pedestalLights[alliance] == true {
|
||||
pedestalLights[alliance] = false
|
||||
fmt.Printf("Clearing %s pedestal\n", alliance)
|
||||
func (lights *Lights) sendLights() {
|
||||
for alliance, connections := range lights.connections {
|
||||
if lights.newConnections || *lights.packets[alliance] != *lights.oldPackets[alliance] {
|
||||
_, err := (*connections).Write(lights.packets[alliance][:])
|
||||
if err != nil {
|
||||
log.Printf("Failed to send %s light packet.", alliance)
|
||||
}
|
||||
mainArena.hotGoalLightNotifier.Notify(lights.hotGoal)
|
||||
}
|
||||
*lights.oldPackets[alliance] = *lights.packets[alliance]
|
||||
}
|
||||
lights.newConnections = false
|
||||
}
|
||||
|
||||
@@ -38,11 +38,14 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
renderSettings(w, r, "Number of alliances must be between 2 and 16.")
|
||||
return
|
||||
}
|
||||
|
||||
eventSettings.NumElimAlliances = numAlliances
|
||||
eventSettings.SelectionRound2Order = r.PostFormValue("selectionRound2Order")
|
||||
eventSettings.SelectionRound3Order = r.PostFormValue("selectionRound3Order")
|
||||
eventSettings.TeamInfoDownloadEnabled = r.PostFormValue("teamInfoDownloadEnabled") == "on"
|
||||
eventSettings.AllianceDisplayHotGoals = r.PostFormValue("allianceDisplayHotGoals") == "on"
|
||||
eventSettings.RedGoalLightsAddress = r.PostFormValue("redGoalLightsAddress")
|
||||
eventSettings.BlueGoalLightsAddress = r.PostFormValue("blueGoalLightsAddress")
|
||||
eventSettings.TbaPublishingEnabled = r.PostFormValue("tbaPublishingEnabled") == "on"
|
||||
eventSettings.TbaEventCode = r.PostFormValue("tbaEventCode")
|
||||
eventSettings.TbaSecretId = r.PostFormValue("tbaSecretId")
|
||||
@@ -58,6 +61,14 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set up the light controller connections again in case the address changed.
|
||||
err = mainArena.lights.SetupConnections()
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/setup/settings", 302)
|
||||
}
|
||||
|
||||
|
||||
@@ -91,12 +91,6 @@
|
||||
<input type="checkbox" name="teamInfoDownloadEnabled"{{if .TeamInfoDownloadEnabled}} checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-7 control-label">Use alliance station displays for hot goals</label>
|
||||
<div class="col-lg-1 checkbox">
|
||||
<input type="checkbox" name="allianceDisplayHotGoals"{{if .AllianceDisplayHotGoals}} checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Publishing</legend>
|
||||
@@ -126,7 +120,26 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Hardware</legend>
|
||||
<legend>Field I/O</legend>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-5 control-label">Red goal LED controller address/port</label>
|
||||
<div class="col-lg-7">
|
||||
<input type="text" class="form-control" name="redGoalLightsAddress" value="{{.RedGoalLightsAddress}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-5 control-label">Blue goal LED controller address/port</label>
|
||||
<div class="col-lg-7">
|
||||
<input type="text" class="form-control" name="blueGoalLightsAddress" value="{{.BlueGoalLightsAddress}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-7 control-label">Show hot goals on alliance station display</label>
|
||||
<div class="col-lg-1 checkbox">
|
||||
<input type="checkbox" name="allianceDisplayHotGoals"{{if .AllianceDisplayHotGoals}} checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<legend>Networking</legend>
|
||||
<p>Enable this setting if you have a Cisco Aironet AP1252AG access point and Catalyst 3500-series
|
||||
switch available, for isolating each team to its own SSID and VLAN.</p>
|
||||
<div class="form-group">
|
||||
|
||||
Reference in New Issue
Block a user