mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Add support for Philips Color Kinetics LEDs in vault.
This commit is contained in:
@@ -29,7 +29,9 @@ CREATE TABLE event_settings (
|
||||
stemtveventcode VARCHAR(16),
|
||||
scaleledaddress VARCHAR(255),
|
||||
redswitchledaddress VARCHAR(255),
|
||||
blueswitchledaddress VARCHAR(255)
|
||||
blueswitchledaddress VARCHAR(255),
|
||||
redvaultledaddress VARCHAR(255),
|
||||
bluevaultledaddress VARCHAR(255)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/Team254/cheesy-arena/led"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/partner"
|
||||
"github.com/Team254/cheesy-arena/vaultled"
|
||||
"log"
|
||||
"math/rand"
|
||||
"time"
|
||||
@@ -83,6 +84,8 @@ type Arena struct {
|
||||
ScaleLeds led.Controller
|
||||
RedSwitchLeds led.Controller
|
||||
BlueSwitchLeds led.Controller
|
||||
RedVaultLeds vaultled.Controller
|
||||
BlueVaultLeds vaultled.Controller
|
||||
warmupLedMode led.Mode
|
||||
lastRedAllianceReady bool
|
||||
lastBlueAllianceReady bool
|
||||
@@ -189,6 +192,12 @@ func (arena *Arena) LoadSettings() error {
|
||||
if err = arena.BlueSwitchLeds.SetAddress(settings.BlueSwitchLedAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = arena.RedVaultLeds.SetAddress(settings.RedVaultLedAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = arena.BlueVaultLeds.SetAddress(settings.BlueVaultLedAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -257,6 +266,8 @@ func (arena *Arena) LoadMatch(match *model.Match) error {
|
||||
arena.ScaleLeds.SetMode(led.OffMode, led.OffMode)
|
||||
arena.RedSwitchLeds.SetMode(led.RedMode, led.RedMode)
|
||||
arena.BlueSwitchLeds.SetMode(led.BlueMode, led.BlueMode)
|
||||
arena.RedVaultLeds.SetAllModes(vaultled.OffMode)
|
||||
arena.BlueVaultLeds.SetAllModes(vaultled.OffMode)
|
||||
arena.lastRedAllianceReady = false
|
||||
arena.lastBlueAllianceReady = false
|
||||
|
||||
@@ -807,6 +818,8 @@ func (arena *Arena) handleLeds() {
|
||||
handleSeesawTeleopLeds(arena.Scale, &arena.ScaleLeds)
|
||||
handleSeesawTeleopLeds(arena.RedSwitch, &arena.RedSwitchLeds)
|
||||
handleSeesawTeleopLeds(arena.BlueSwitch, &arena.BlueSwitchLeds)
|
||||
handleVaultTeleopLeds(arena.RedVault, &arena.RedVaultLeds)
|
||||
handleVaultTeleopLeds(arena.BlueVault, &arena.BlueVaultLeds)
|
||||
case PausePeriod:
|
||||
arena.ScaleLeds.SetMode(led.OffMode, led.OffMode)
|
||||
arena.RedSwitchLeds.SetMode(led.OffMode, led.OffMode)
|
||||
@@ -822,11 +835,15 @@ func (arena *Arena) handleLeds() {
|
||||
arena.ScaleLeds.SetMode(mode, mode)
|
||||
arena.RedSwitchLeds.SetMode(mode, mode)
|
||||
arena.BlueSwitchLeds.SetMode(mode, mode)
|
||||
arena.RedVaultLeds.SetAllModes(vaultled.OffMode)
|
||||
arena.BlueVaultLeds.SetAllModes(vaultled.OffMode)
|
||||
}
|
||||
|
||||
arena.ScaleLeds.Update()
|
||||
arena.RedSwitchLeds.Update()
|
||||
arena.BlueSwitchLeds.Update()
|
||||
arena.RedVaultLeds.Update()
|
||||
arena.BlueVaultLeds.Update()
|
||||
}
|
||||
|
||||
func handleSeesawTeleopLeds(seesaw *game.Seesaw, leds *led.Controller) {
|
||||
@@ -867,6 +884,33 @@ func handleSeesawTeleopLeds(seesaw *game.Seesaw, leds *led.Controller) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleVaultTeleopLeds(vault *game.Vault, leds *vaultled.Controller) {
|
||||
playedMode := vaultled.RedPlayedMode
|
||||
if vault.Alliance == game.BlueAlliance {
|
||||
playedMode = vaultled.BluePlayedMode
|
||||
}
|
||||
cubesModeMap := map[int]vaultled.Mode{0: vaultled.OffMode, 1: vaultled.OneCubeMode, 2: vaultled.TwoCubeMode,
|
||||
3: vaultled.ThreeCubeMode}
|
||||
|
||||
if vault.ForcePowerUp != nil {
|
||||
leds.SetForceMode(playedMode)
|
||||
} else {
|
||||
leds.SetForceMode(cubesModeMap[vault.ForceCubes])
|
||||
}
|
||||
|
||||
if vault.LevitatePlayed {
|
||||
leds.SetLevitateMode(playedMode)
|
||||
} else {
|
||||
leds.SetLevitateMode(cubesModeMap[vault.LevitateCubes])
|
||||
}
|
||||
|
||||
if vault.BoostPowerUp != nil {
|
||||
leds.SetBoostMode(playedMode)
|
||||
} else {
|
||||
leds.SetBoostMode(cubesModeMap[vault.BoostCubes])
|
||||
}
|
||||
}
|
||||
|
||||
func (arena *Arena) handleEstop(station string, state bool) {
|
||||
allianceStation := arena.AllianceStations[station]
|
||||
if state {
|
||||
|
||||
56
led/color.go
56
led/color.go
@@ -5,36 +5,36 @@
|
||||
|
||||
package led
|
||||
|
||||
type color int
|
||||
type Color int
|
||||
|
||||
const (
|
||||
red color = iota
|
||||
orange
|
||||
yellow
|
||||
green
|
||||
teal
|
||||
blue
|
||||
purple
|
||||
white
|
||||
black
|
||||
purpleRed
|
||||
purpleBlue
|
||||
dimRed
|
||||
dimBlue
|
||||
Red Color = iota
|
||||
Orange
|
||||
Yellow
|
||||
Green
|
||||
Teal
|
||||
Blue
|
||||
Purple
|
||||
White
|
||||
Black
|
||||
PurpleRed
|
||||
PurpleBlue
|
||||
DimRed
|
||||
DimBlue
|
||||
)
|
||||
|
||||
var colors = map[color][3]byte{
|
||||
red: {255, 0, 0},
|
||||
orange: {255, 50, 0},
|
||||
yellow: {255, 255, 0},
|
||||
green: {0, 255, 0},
|
||||
teal: {0, 100, 100},
|
||||
blue: {0, 0, 255},
|
||||
purple: {100, 0, 100},
|
||||
white: {255, 255, 255},
|
||||
black: {0, 0, 0},
|
||||
purpleRed: {200, 0, 50},
|
||||
purpleBlue: {50, 0, 200},
|
||||
dimRed: {50, 0, 0},
|
||||
dimBlue: {0, 0, 50},
|
||||
var Colors = map[Color][3]byte{
|
||||
Red: {255, 0, 0},
|
||||
Orange: {255, 50, 0},
|
||||
Yellow: {255, 255, 0},
|
||||
Green: {0, 255, 0},
|
||||
Teal: {0, 100, 100},
|
||||
Blue: {0, 0, 255},
|
||||
Purple: {100, 0, 100},
|
||||
White: {255, 255, 255},
|
||||
Black: {0, 0, 0},
|
||||
PurpleRed: {200, 0, 50},
|
||||
PurpleBlue: {50, 0, 200},
|
||||
DimRed: {50, 0, 0},
|
||||
DimBlue: {0, 0, 50},
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ func (controller *Controller) Update() error {
|
||||
// Constructs the structure of an E1.31 data packet that can be re-used indefinitely by updating the pixel data and
|
||||
// re-sending it.
|
||||
func createBlankPacket(numPixels int) []byte {
|
||||
size := 126 + 3*numPixels
|
||||
size := pixelDataOffset + 3*numPixels
|
||||
packet := make([]byte, size)
|
||||
|
||||
// Preamble size
|
||||
|
||||
124
led/strip.go
124
led/strip.go
@@ -23,15 +23,15 @@ type strip struct {
|
||||
func (strip *strip) updatePixels() {
|
||||
switch strip.currentMode {
|
||||
case RedMode:
|
||||
strip.updateSingleColorMode(red)
|
||||
strip.updateSingleColorMode(Red)
|
||||
case GreenMode:
|
||||
strip.updateSingleColorMode(green)
|
||||
strip.updateSingleColorMode(Green)
|
||||
case BlueMode:
|
||||
strip.updateSingleColorMode(blue)
|
||||
strip.updateSingleColorMode(Blue)
|
||||
case WhiteMode:
|
||||
strip.updateSingleColorMode(white)
|
||||
strip.updateSingleColorMode(White)
|
||||
case PurpleMode:
|
||||
strip.updateSingleColorMode(purple)
|
||||
strip.updateSingleColorMode(Purple)
|
||||
case ChaseMode:
|
||||
strip.updateChaseMode()
|
||||
case WarmupMode:
|
||||
@@ -90,43 +90,43 @@ func (strip *strip) populatePacketPixels(pixelData []byte) {
|
||||
}
|
||||
|
||||
// Returns the primary color (red or blue) of this strip.
|
||||
func (strip *strip) getColor() color {
|
||||
func (strip *strip) getColor() Color {
|
||||
if strip.isRed {
|
||||
return red
|
||||
return Red
|
||||
}
|
||||
return blue
|
||||
return Blue
|
||||
}
|
||||
|
||||
// Returns the opposite primary color (red or blue) to this strip.
|
||||
func (strip *strip) getOppositeColor() color {
|
||||
func (strip *strip) getOppositeColor() Color {
|
||||
if strip.isRed {
|
||||
return blue
|
||||
return Blue
|
||||
}
|
||||
return red
|
||||
return Red
|
||||
}
|
||||
|
||||
// Returns a color partway between purple and the primary color (red or blue) of this strip.
|
||||
func (strip *strip) getMidColor() color {
|
||||
func (strip *strip) getMidColor() Color {
|
||||
if strip.isRed {
|
||||
return purpleBlue
|
||||
return PurpleBlue
|
||||
}
|
||||
return purpleRed
|
||||
return PurpleRed
|
||||
}
|
||||
|
||||
// Returns a dim version of the primary color (red or blue) of this strip.
|
||||
func (strip *strip) getDimColor() color {
|
||||
func (strip *strip) getDimColor() Color {
|
||||
if strip.isRed {
|
||||
return dimRed
|
||||
return DimRed
|
||||
}
|
||||
return dimBlue
|
||||
return DimBlue
|
||||
}
|
||||
|
||||
// Returns a dim version of the opposite primary color (red or blue) of this strip.
|
||||
func (strip *strip) getDimOppositeColor() color {
|
||||
func (strip *strip) getDimOppositeColor() Color {
|
||||
if strip.isRed {
|
||||
return dimBlue
|
||||
return DimBlue
|
||||
}
|
||||
return dimRed
|
||||
return DimRed
|
||||
}
|
||||
|
||||
// Returns the starting offset for the gradient mode for this strip.
|
||||
@@ -139,23 +139,23 @@ func (strip *strip) getGradientStartOffset() int {
|
||||
|
||||
func (strip *strip) updateOffMode() {
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = colors[black]
|
||||
strip.pixels[i] = Colors[Black]
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *strip) updateSingleColorMode(color color) {
|
||||
func (strip *strip) updateSingleColorMode(color Color) {
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = colors[color]
|
||||
strip.pixels[i] = Colors[color]
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *strip) updateChaseMode() {
|
||||
if strip.counter == int(black)*numPixels { // Ignore colors listed after white.
|
||||
if strip.counter == int(Black)*numPixels { // Ignore colors listed after white.
|
||||
strip.counter = 0
|
||||
}
|
||||
color := color(strip.counter / numPixels)
|
||||
color := Color(strip.counter / numPixels)
|
||||
pixelIndex := strip.counter % numPixels
|
||||
strip.pixels[pixelIndex] = colors[color]
|
||||
strip.pixels[pixelIndex] = Colors[color]
|
||||
}
|
||||
|
||||
func (strip *strip) updateWarmupMode() {
|
||||
@@ -163,14 +163,14 @@ func (strip *strip) updateWarmupMode() {
|
||||
if strip.counter == 0 {
|
||||
// Show solid white to start.
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = colors[white]
|
||||
strip.pixels[i] = Colors[White]
|
||||
}
|
||||
} else if strip.counter <= endCounter {
|
||||
// Build to the alliance color from each side.
|
||||
numLitPixels := numPixels / 2 * strip.counter / endCounter
|
||||
for i := 0; i < numLitPixels; i++ {
|
||||
strip.pixels[i] = colors[strip.getColor()]
|
||||
strip.pixels[numPixels-i-1] = colors[strip.getColor()]
|
||||
strip.pixels[i] = Colors[strip.getColor()]
|
||||
strip.pixels[numPixels-i-1] = Colors[strip.getColor()]
|
||||
}
|
||||
} else {
|
||||
// Prevent the counter from rolling over.
|
||||
@@ -184,11 +184,11 @@ func (strip *strip) updateWarmup2Mode() {
|
||||
if strip.counter < startCounter {
|
||||
// Show solid purple to start.
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = colors[purple]
|
||||
strip.pixels[i] = Colors[Purple]
|
||||
}
|
||||
} else if strip.counter <= endCounter {
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = getFadeColor(purple, strip.getColor(), strip.counter-startCounter,
|
||||
strip.pixels[i] = getFadeColor(Purple, strip.getColor(), strip.counter-startCounter,
|
||||
endCounter-startCounter)
|
||||
}
|
||||
} else {
|
||||
@@ -204,11 +204,11 @@ func (strip *strip) updateWarmup3Mode() {
|
||||
if strip.counter < startCounter {
|
||||
// Show solid purple to start.
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = colors[purple]
|
||||
strip.pixels[i] = Colors[Purple]
|
||||
}
|
||||
} else if strip.counter < middleCounter {
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = getFadeColor(purple, strip.getMidColor(), strip.counter-startCounter,
|
||||
strip.pixels[i] = getFadeColor(Purple, strip.getMidColor(), strip.counter-startCounter,
|
||||
middleCounter-startCounter)
|
||||
}
|
||||
} else if strip.counter <= endCounter {
|
||||
@@ -230,7 +230,7 @@ func (strip *strip) updateWarmup4Mode() {
|
||||
if strip.counter >= middleCounter {
|
||||
for i := 0; i < numPixels; i++ {
|
||||
if i < strip.counter-middleCounter {
|
||||
strip.pixels[i] = colors[strip.getColor()]
|
||||
strip.pixels[i] = Colors[strip.getColor()]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,16 +247,16 @@ func (strip *strip) updateOwnedMode() {
|
||||
case 0:
|
||||
fallthrough
|
||||
case 1:
|
||||
strip.pixels[len(strip.pixels)-i-1] = colors[strip.getColor()]
|
||||
strip.pixels[len(strip.pixels)-i-1] = Colors[strip.getColor()]
|
||||
default:
|
||||
strip.pixels[len(strip.pixels)-i-1] = colors[black]
|
||||
strip.pixels[len(strip.pixels)-i-1] = Colors[Black]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *strip) updateNotOwnedMode() {
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = colors[strip.getDimColor()]
|
||||
strip.pixels[i] = Colors[strip.getDimColor()]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,11 +271,11 @@ func (strip *strip) updateForceMode() {
|
||||
case 2:
|
||||
fallthrough
|
||||
case 4:
|
||||
strip.pixels[i] = colors[strip.getColor()]
|
||||
strip.pixels[i] = Colors[strip.getColor()]
|
||||
case 3:
|
||||
strip.pixels[i] = colors[strip.getDimOppositeColor()]
|
||||
strip.pixels[i] = Colors[strip.getDimOppositeColor()]
|
||||
default:
|
||||
strip.pixels[i] = colors[black]
|
||||
strip.pixels[i] = Colors[Black]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,9 +288,9 @@ func (strip *strip) updateBoostMode() {
|
||||
}
|
||||
for i := 0; i < numPixels; i++ {
|
||||
if i%pixelSpacing == strip.counter/speedDivisor%pixelSpacing {
|
||||
strip.pixels[i] = colors[strip.getColor()]
|
||||
strip.pixels[i] = Colors[strip.getColor()]
|
||||
} else {
|
||||
strip.pixels[i] = colors[black]
|
||||
strip.pixels[i] = Colors[Black]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,7 +300,7 @@ func (strip *strip) updateRandomMode() {
|
||||
return
|
||||
}
|
||||
for i := 0; i < numPixels; i++ {
|
||||
strip.pixels[i] = colors[color(rand.Intn(int(black)))] // Ignore colors listed after white.
|
||||
strip.pixels[i] = Colors[Color(rand.Intn(int(Black)))] // Ignore colors listed after white.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,21 +313,21 @@ func (strip *strip) updateFadeRedBlueMode() {
|
||||
|
||||
for i := 0; i < numPixels; i++ {
|
||||
if strip.counter < holdCycles {
|
||||
strip.pixels[i] = colors[black]
|
||||
strip.pixels[i] = Colors[Black]
|
||||
} else if strip.counter < holdCycles+fadeCycles {
|
||||
strip.pixels[i] = getFadeColor(black, red, strip.counter-holdCycles, fadeCycles)
|
||||
strip.pixels[i] = getFadeColor(Black, Red, strip.counter-holdCycles, fadeCycles)
|
||||
} else if strip.counter < 2*holdCycles+fadeCycles {
|
||||
strip.pixels[i] = colors[red]
|
||||
strip.pixels[i] = Colors[Red]
|
||||
} else if strip.counter < 2*holdCycles+2*fadeCycles {
|
||||
strip.pixels[i] = getFadeColor(red, black, strip.counter-2*holdCycles-fadeCycles, fadeCycles)
|
||||
strip.pixels[i] = getFadeColor(Red, Black, strip.counter-2*holdCycles-fadeCycles, fadeCycles)
|
||||
} else if strip.counter < 3*holdCycles+2*fadeCycles {
|
||||
strip.pixels[i] = colors[black]
|
||||
strip.pixels[i] = Colors[Black]
|
||||
} else if strip.counter < 3*holdCycles+3*fadeCycles {
|
||||
strip.pixels[i] = getFadeColor(black, blue, strip.counter-3*holdCycles-2*fadeCycles, fadeCycles)
|
||||
strip.pixels[i] = getFadeColor(Black, Blue, strip.counter-3*holdCycles-2*fadeCycles, fadeCycles)
|
||||
} else if strip.counter < 4*holdCycles+3*fadeCycles {
|
||||
strip.pixels[i] = colors[blue]
|
||||
strip.pixels[i] = Colors[Blue]
|
||||
} else if strip.counter < 4*holdCycles+4*fadeCycles {
|
||||
strip.pixels[i] = getFadeColor(blue, black, strip.counter-4*holdCycles-3*fadeCycles, fadeCycles)
|
||||
strip.pixels[i] = getFadeColor(Blue, Black, strip.counter-4*holdCycles-3*fadeCycles, fadeCycles)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,11 +341,11 @@ func (strip *strip) updateFadeSingleMode() {
|
||||
|
||||
for i := 0; i < numPixels; i++ {
|
||||
if strip.counter < offCycles {
|
||||
strip.pixels[i] = colors[black]
|
||||
strip.pixels[i] = Colors[Black]
|
||||
} else if strip.counter < offCycles+fadeCycles {
|
||||
strip.pixels[i] = getFadeColor(black, strip.getColor(), strip.counter-offCycles, fadeCycles)
|
||||
strip.pixels[i] = getFadeColor(Black, strip.getColor(), strip.counter-offCycles, fadeCycles)
|
||||
} else if strip.counter < offCycles+2*fadeCycles {
|
||||
strip.pixels[i] = getFadeColor(strip.getColor(), black, strip.counter-offCycles-fadeCycles, fadeCycles)
|
||||
strip.pixels[i] = getFadeColor(strip.getColor(), Black, strip.counter-offCycles-fadeCycles, fadeCycles)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,17 +360,17 @@ func (strip *strip) updateBlinkMode() {
|
||||
divisor := 10
|
||||
for i := 0; i < numPixels; i++ {
|
||||
if strip.counter%divisor < divisor/2 {
|
||||
strip.pixels[i] = colors[white]
|
||||
strip.pixels[i] = Colors[White]
|
||||
} else {
|
||||
strip.pixels[i] = colors[black]
|
||||
strip.pixels[i] = Colors[Black]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolates between the two colors based on the given fraction.
|
||||
func getFadeColor(fromColor, toColor color, numerator, denominator int) [3]byte {
|
||||
from := colors[fromColor]
|
||||
to := colors[toColor]
|
||||
func getFadeColor(fromColor, toColor Color, numerator, denominator int) [3]byte {
|
||||
from := Colors[fromColor]
|
||||
to := Colors[toColor]
|
||||
var fadeColor [3]byte
|
||||
for i := 0; i < 3; i++ {
|
||||
fadeColor[i] = byte(int(from[i]) + numerator*(int(to[i])-int(from[i]))/denominator)
|
||||
@@ -382,10 +382,10 @@ func getFadeColor(fromColor, toColor color, numerator, denominator int) [3]byte
|
||||
func getGradientColor(offset, numPixels int) [3]byte {
|
||||
offset %= numPixels
|
||||
if 3*offset < numPixels {
|
||||
return getFadeColor(red, green, 3*offset, numPixels)
|
||||
return getFadeColor(Red, Green, 3*offset, numPixels)
|
||||
} else if 3*offset < 2*numPixels {
|
||||
return getFadeColor(green, blue, 3*offset-numPixels, numPixels)
|
||||
return getFadeColor(Green, Blue, 3*offset-numPixels, numPixels)
|
||||
} else {
|
||||
return getFadeColor(blue, red, 3*offset-2*numPixels, numPixels)
|
||||
return getFadeColor(Blue, Red, 3*offset-2*numPixels, numPixels)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ type EventSettings struct {
|
||||
ScaleLedAddress string
|
||||
RedSwitchLedAddress string
|
||||
BlueSwitchLedAddress string
|
||||
RedVaultLedAddress string
|
||||
BlueVaultLedAddress string
|
||||
}
|
||||
|
||||
const eventSettingsId = 0
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<legend>LEDs</legend>
|
||||
<form class="" action="/setup/field/test" method="POST">
|
||||
<div class="form-group">
|
||||
<label>Switch/Scale</label>
|
||||
{{range $i, $name := .LedModeNames}}
|
||||
<div class="radio">
|
||||
<label>
|
||||
@@ -95,6 +96,17 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Vault</label>
|
||||
{{range $i, $name := .VaultLedModeNames}}
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="vaultMode" value="{{$i}}" onclick="this.form.submit()"
|
||||
{{if eq $.CurrentVaultLedMode $i}}checked{{end}}>{{$name}}
|
||||
</label>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -259,6 +259,18 @@
|
||||
<input type="text" class="form-control" name="blueSwitchLedAddress" value="{{.BlueSwitchLedAddress}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-5 control-label">Red Vault Controller Address</label>
|
||||
<div class="col-lg-7">
|
||||
<input type="text" class="form-control" name="redVaultLedAddress" value="{{.RedVaultLedAddress}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-5 control-label">Blue Vault Controller Address</label>
|
||||
<div class="col-lg-7">
|
||||
<input type="text" class="form-control" name="blueVaultLedAddress" value="{{.BlueVaultLedAddress}}">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-7 col-lg-offset-5">
|
||||
|
||||
199
vaultled/controller.go
Normal file
199
vaultled/controller.go
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright 2018 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Represents a Philips Color Kinetics LED controller with one output, as used in the 2018 vault.
|
||||
|
||||
package vaultled
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/led"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
port = 6038
|
||||
packetTimeoutSec = 1
|
||||
numPixels = 17
|
||||
pixelDataOffset = 21
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
CurrentForceMode Mode
|
||||
CurrentLevitateMode Mode
|
||||
CurrentBoostMode Mode
|
||||
pixels [numPixels][3]byte
|
||||
oldPixels [numPixels][3]byte
|
||||
conn net.Conn
|
||||
packet []byte
|
||||
lastPacketTime time.Time
|
||||
}
|
||||
|
||||
func (controller *Controller) SetAddress(address string) error {
|
||||
if controller.conn != nil {
|
||||
controller.conn.Close()
|
||||
controller.conn = nil
|
||||
}
|
||||
|
||||
if address != "" {
|
||||
var err error
|
||||
if controller.conn, err = net.Dial("udp4", fmt.Sprintf("%s:%d", address, port)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the current mode for the section of LEDs corresponding to the force powerup.
|
||||
func (controller *Controller) SetForceMode(mode Mode) {
|
||||
controller.CurrentForceMode = mode
|
||||
controller.setPixels(0, mode)
|
||||
}
|
||||
|
||||
// Sets the current mode for the section of LEDs corresponding to the levitate powerup.
|
||||
func (controller *Controller) SetLevitateMode(mode Mode) {
|
||||
controller.CurrentLevitateMode = mode
|
||||
controller.setPixels(6, mode)
|
||||
}
|
||||
|
||||
// Sets the current mode for the section of LEDs corresponding to the boost powerup.
|
||||
func (controller *Controller) SetBoostMode(mode Mode) {
|
||||
controller.CurrentBoostMode = mode
|
||||
controller.setPixels(12, mode)
|
||||
}
|
||||
|
||||
// Sets the current mode for all sections of LEDs to the same value.
|
||||
func (controller *Controller) SetAllModes(mode Mode) {
|
||||
controller.SetForceMode(mode)
|
||||
controller.SetLevitateMode(mode)
|
||||
controller.SetBoostMode(mode)
|
||||
}
|
||||
|
||||
// Sends a packet if necessary. Should be called from a timed loop.
|
||||
func (controller *Controller) Update() error {
|
||||
if controller.conn == nil {
|
||||
// This controller is not configured; do nothing.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create the template packet if it doesn't already exist.
|
||||
if len(controller.packet) == 0 {
|
||||
controller.packet = createBlankPacket(numPixels)
|
||||
}
|
||||
|
||||
// Send packets if the pixel values have changed.
|
||||
if controller.shouldSendPacket() {
|
||||
controller.populatePacketPixels(controller.packet[pixelDataOffset:])
|
||||
controller.sendPacket()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (controller *Controller) setPixels(offset int, mode Mode) {
|
||||
for i := 0; i < 5; i++ {
|
||||
controller.pixels[offset+i] = led.Colors[led.Black]
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case ThreeCubeMode:
|
||||
controller.pixels[offset+3] = led.Colors[led.Yellow]
|
||||
fallthrough
|
||||
case TwoCubeMode:
|
||||
controller.pixels[offset+2] = led.Colors[led.Yellow]
|
||||
fallthrough
|
||||
case OneCubeMode:
|
||||
controller.pixels[offset+1] = led.Colors[led.Yellow]
|
||||
case RedPlayedMode:
|
||||
for i := 0; i < 5; i++ {
|
||||
controller.pixels[offset+i] = led.Colors[led.Red]
|
||||
}
|
||||
case BluePlayedMode:
|
||||
for i := 0; i < 5; i++ {
|
||||
controller.pixels[offset+i] = led.Colors[led.Blue]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs the structure of a KiNET data packet that can be re-used indefinitely by updating the pixel data and
|
||||
// re-sending it.
|
||||
func createBlankPacket(numPixels int) []byte {
|
||||
size := pixelDataOffset + 3*numPixels
|
||||
packet := make([]byte, size)
|
||||
|
||||
// Magic sequence
|
||||
packet[0] = 0x04
|
||||
packet[1] = 0x01
|
||||
packet[2] = 0xdc
|
||||
packet[3] = 0x4a
|
||||
|
||||
// Version
|
||||
packet[4] = 0x01
|
||||
packet[5] = 0x00
|
||||
|
||||
// Type
|
||||
packet[6] = 0x01
|
||||
packet[7] = 0x01
|
||||
|
||||
// Sequence
|
||||
packet[8] = 0x00
|
||||
packet[9] = 0x00
|
||||
packet[10] = 0x00
|
||||
packet[11] = 0x00
|
||||
|
||||
// Port
|
||||
packet[12] = 0x00
|
||||
|
||||
// Padding
|
||||
packet[13] = 0x00
|
||||
|
||||
// Flags
|
||||
packet[14] = 0x00
|
||||
packet[15] = 0x00
|
||||
|
||||
// Timer
|
||||
packet[16] = 0xff
|
||||
packet[17] = 0xff
|
||||
packet[18] = 0xff
|
||||
packet[19] = 0xff
|
||||
|
||||
// Universe
|
||||
packet[20] = 0x00
|
||||
|
||||
// Remainder of packet is pixel data which will be populated whenever packet is sent.
|
||||
return packet
|
||||
}
|
||||
|
||||
// Returns true if the pixel data has changed.
|
||||
func (controller *Controller) shouldSendPacket() bool {
|
||||
for i := 0; i < numPixels; i++ {
|
||||
if controller.pixels[i] != controller.oldPixels[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return time.Since(controller.lastPacketTime).Seconds() > packetTimeoutSec
|
||||
}
|
||||
|
||||
// Writes the pixel RGB values into the given packet in preparation for sending.
|
||||
func (controller *Controller) populatePacketPixels(pixelData []byte) {
|
||||
for i, pixel := range controller.pixels {
|
||||
pixelData[3*i] = pixel[0]
|
||||
pixelData[3*i+1] = pixel[1]
|
||||
pixelData[3*i+2] = pixel[2]
|
||||
}
|
||||
|
||||
// Keep a record of the pixel values in order to detect future changes.
|
||||
controller.oldPixels = controller.pixels
|
||||
controller.lastPacketTime = time.Now()
|
||||
}
|
||||
|
||||
func (controller *Controller) sendPacket() error {
|
||||
_, err := controller.conn.Write(controller.packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
26
vaultled/mode.go
Normal file
26
vaultled/mode.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2018 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Contains the set of display modes for the vault LEDs.
|
||||
|
||||
package vaultled
|
||||
|
||||
type Mode int
|
||||
|
||||
const (
|
||||
OffMode Mode = iota
|
||||
OneCubeMode
|
||||
TwoCubeMode
|
||||
ThreeCubeMode
|
||||
RedPlayedMode
|
||||
BluePlayedMode
|
||||
)
|
||||
|
||||
var ModeNames = map[Mode]string{
|
||||
OffMode: "Off",
|
||||
OneCubeMode: "One Cube",
|
||||
TwoCubeMode: "Two Cubes",
|
||||
ThreeCubeMode: "Three Cubes",
|
||||
RedPlayedMode: "Red Played",
|
||||
BluePlayedMode: "Blue Played",
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/Team254/cheesy-arena/led"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/vaultled"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -34,8 +35,11 @@ func (web *Web) fieldGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
CoilNames []string
|
||||
CurrentLedMode led.Mode
|
||||
LedModeNames map[led.Mode]string
|
||||
CurrentVaultLedMode vaultled.Mode
|
||||
VaultLedModeNames map[vaultled.Mode]string
|
||||
}{web.arena.EventSettings, web.arena.AllianceStationDisplays, plc.GetInputNames(), plc.GetRegisterNames(),
|
||||
plc.GetCoilNames(), web.arena.ScaleLeds.GetCurrentMode(), led.ModeNames}
|
||||
plc.GetCoilNames(), web.arena.ScaleLeds.GetCurrentMode(), led.ModeNames,
|
||||
web.arena.RedVaultLeds.CurrentForceMode, vaultled.ModeNames}
|
||||
err = template.ExecuteTemplate(w, "base", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
@@ -83,6 +87,11 @@ func (web *Web) fieldTestPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
web.arena.RedSwitchLeds.SetMode(ledMode, ledMode)
|
||||
web.arena.BlueSwitchLeds.SetMode(ledMode, ledMode)
|
||||
|
||||
vaultMode, _ := strconv.Atoi(r.PostFormValue("vaultMode"))
|
||||
vaultLedMode := vaultled.Mode(vaultMode)
|
||||
web.arena.RedVaultLeds.SetAllModes(vaultLedMode)
|
||||
web.arena.BlueVaultLeds.SetAllModes(vaultLedMode)
|
||||
|
||||
http.Redirect(w, r, "/setup/field", 303)
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
eventSettings.ScaleLedAddress = r.PostFormValue("scaleLedAddress")
|
||||
eventSettings.RedSwitchLedAddress = r.PostFormValue("redSwitchLedAddress")
|
||||
eventSettings.BlueSwitchLedAddress = r.PostFormValue("blueSwitchLedAddress")
|
||||
eventSettings.RedVaultLedAddress = r.PostFormValue("redVaultLedAddress")
|
||||
eventSettings.BlueVaultLedAddress = r.PostFormValue("blueVaultLedAddress")
|
||||
|
||||
err := web.arena.Database.SaveEventSettings(eventSettings)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user