WIP: Light integration.

This commit is contained in:
Patrick Fairbank
2014-08-23 21:42:23 -07:00
parent 777eef3723
commit 40ffe03158
6 changed files with 174 additions and 58 deletions

View File

@@ -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)
}
}

View File

@@ -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),

View File

@@ -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
View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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">