From 635ce728063dd341e2ad63cb6ac5017541563e5f Mon Sep 17 00:00:00 2001 From: Patrick Fairbank Date: Sun, 19 Aug 2018 02:00:50 -0700 Subject: [PATCH] Add support for Philips Color Kinetics LEDs in vault. --- .../20140524160241_CreateEventSettings.sql | 4 +- field/arena.go | 44 ++++ led/color.go | 56 ++--- led/controller.go | 2 +- led/strip.go | 124 +++++------ model/event_settings.go | 2 + templates/setup_field.html | 12 ++ templates/setup_settings.html | 12 ++ vaultled/controller.go | 199 ++++++++++++++++++ vaultled/mode.go | 26 +++ web/setup_field.go | 11 +- web/setup_settings.go | 2 + 12 files changed, 401 insertions(+), 93 deletions(-) create mode 100644 vaultled/controller.go create mode 100644 vaultled/mode.go diff --git a/db/migrations/20140524160241_CreateEventSettings.sql b/db/migrations/20140524160241_CreateEventSettings.sql index 61ba51d..1ca267c 100644 --- a/db/migrations/20140524160241_CreateEventSettings.sql +++ b/db/migrations/20140524160241_CreateEventSettings.sql @@ -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 diff --git a/field/arena.go b/field/arena.go index 9c05e74..7b3145b 100644 --- a/field/arena.go +++ b/field/arena.go @@ -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 { diff --git a/led/color.go b/led/color.go index ac1b288..8a40b26 100644 --- a/led/color.go +++ b/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}, } diff --git a/led/controller.go b/led/controller.go index b4cfe92..7978ca5 100644 --- a/led/controller.go +++ b/led/controller.go @@ -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 diff --git a/led/strip.go b/led/strip.go index 27aad28..87f6e35 100644 --- a/led/strip.go +++ b/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) } } diff --git a/model/event_settings.go b/model/event_settings.go index f017176..8476a02 100644 --- a/model/event_settings.go +++ b/model/event_settings.go @@ -35,6 +35,8 @@ type EventSettings struct { ScaleLedAddress string RedSwitchLedAddress string BlueSwitchLedAddress string + RedVaultLedAddress string + BlueVaultLedAddress string } const eventSettingsId = 0 diff --git a/templates/setup_field.html b/templates/setup_field.html index 55453d7..834283a 100644 --- a/templates/setup_field.html +++ b/templates/setup_field.html @@ -86,6 +86,7 @@ LEDs
+ {{range $i, $name := .LedModeNames}}
{{end}}
+
+ + {{range $i, $name := .VaultLedModeNames}} +
+ +
+ {{end}} +
diff --git a/templates/setup_settings.html b/templates/setup_settings.html index a421232..e0fc9eb 100644 --- a/templates/setup_settings.html +++ b/templates/setup_settings.html @@ -259,6 +259,18 @@ +
+ +
+ +
+
+
+ +
+ +
+
diff --git a/vaultled/controller.go b/vaultled/controller.go new file mode 100644 index 0000000..1bdea42 --- /dev/null +++ b/vaultled/controller.go @@ -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 +} diff --git a/vaultled/mode.go b/vaultled/mode.go new file mode 100644 index 0000000..32a9518 --- /dev/null +++ b/vaultled/mode.go @@ -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", +} diff --git a/web/setup_field.go b/web/setup_field.go index 1b4a891..6fdfff5 100644 --- a/web/setup_field.go +++ b/web/setup_field.go @@ -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) } diff --git a/web/setup_settings.go b/web/setup_settings.go index d695f3e..0126c05 100644 --- a/web/setup_settings.go +++ b/web/setup_settings.go @@ -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 {