mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Remove LED code since it's unused in 2019.
Revert this commit to bring it back in a future year.
This commit is contained in:
@@ -20,7 +20,6 @@ type ArenaNotifiers struct {
|
||||
ArenaStatusNotifier *websocket.Notifier
|
||||
AudienceDisplayModeNotifier *websocket.Notifier
|
||||
DisplayConfigurationNotifier *websocket.Notifier
|
||||
LedModeNotifier *websocket.Notifier
|
||||
LowerThirdNotifier *websocket.Notifier
|
||||
MatchLoadNotifier *websocket.Notifier
|
||||
MatchTimeNotifier *websocket.Notifier
|
||||
@@ -37,9 +36,6 @@ type DisplayConfigurationMessage struct {
|
||||
DisplayUrls map[string]string
|
||||
}
|
||||
|
||||
type LedModeMessage struct {
|
||||
}
|
||||
|
||||
type MatchTimeMessage struct {
|
||||
MatchState
|
||||
MatchTimeSec int
|
||||
@@ -61,7 +57,6 @@ func (arena *Arena) configureNotifiers() {
|
||||
arena.generateAudienceDisplayModeMessage)
|
||||
arena.DisplayConfigurationNotifier = websocket.NewNotifier("displayConfiguration",
|
||||
arena.generateDisplayConfigurationMessage)
|
||||
arena.LedModeNotifier = websocket.NewNotifier("ledMode", arena.generateLedModeMessage)
|
||||
arena.LowerThirdNotifier = websocket.NewNotifier("lowerThird", arena.generateLowerThirdMessage)
|
||||
arena.MatchLoadNotifier = websocket.NewNotifier("matchLoad", arena.generateMatchLoadMessage)
|
||||
arena.MatchTimeNotifier = websocket.NewNotifier("matchTime", arena.generateMatchTimeMessage)
|
||||
@@ -118,10 +113,6 @@ func (arena *Arena) generateDisplayConfigurationMessage() interface{} {
|
||||
return &DisplayConfigurationMessage{displaysCopy, displayUrls}
|
||||
}
|
||||
|
||||
func (arena *Arena) generateLedModeMessage() interface{} {
|
||||
return &LedModeMessage{}
|
||||
}
|
||||
|
||||
func (arena *Arena) generateLowerThirdMessage() interface{} {
|
||||
return arena.LowerThird
|
||||
}
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
// Copyright 2018 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Represents an E1.31 sACN (DMX over Ethernet) LED controller with four outputs.
|
||||
|
||||
package led
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
advatekPort = 5568
|
||||
advatekSourceName = "Cheesy Arena"
|
||||
advatekPacketTimeoutSec = 1
|
||||
advatekNumPixels = 114
|
||||
advatekPixelDataOffset = 126
|
||||
advatekNearStripUniverse = 1
|
||||
advatekFarStripUniverse = 2
|
||||
)
|
||||
|
||||
type AdvatekController struct {
|
||||
nearStrip advatekStrip
|
||||
farStrip advatekStrip
|
||||
conn net.Conn
|
||||
packet []byte
|
||||
}
|
||||
|
||||
func (controller *AdvatekController) 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, advatekPort)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the current LED sequence mode and resets the intra-sequence counter to the beginning.
|
||||
func (controller *AdvatekController) SetMode(nearMode, farMode StripMode) {
|
||||
if nearMode != controller.nearStrip.currentMode {
|
||||
controller.nearStrip.currentMode = nearMode
|
||||
controller.nearStrip.counter = 0
|
||||
}
|
||||
if farMode != controller.farStrip.currentMode {
|
||||
controller.farStrip.currentMode = farMode
|
||||
controller.farStrip.counter = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the current mode if both sides are in the same mode, or off otherwise.
|
||||
func (controller *AdvatekController) GetCurrentMode() StripMode {
|
||||
if controller.nearStrip.currentMode == controller.farStrip.currentMode {
|
||||
return controller.nearStrip.currentMode
|
||||
} else {
|
||||
return OffMode
|
||||
}
|
||||
}
|
||||
|
||||
// Advances the pixel values through the current sequence and sends a packet if necessary. Should be called from a timed
|
||||
// loop.
|
||||
func (controller *AdvatekController) Update() error {
|
||||
if controller.conn == nil {
|
||||
// This controller is not configured; do nothing.
|
||||
return nil
|
||||
}
|
||||
|
||||
controller.nearStrip.updatePixels()
|
||||
controller.farStrip.updatePixels()
|
||||
|
||||
// Create the template packet if it doesn't already exist.
|
||||
if len(controller.packet) == 0 {
|
||||
controller.packet = createBlankAdvatekPacket(advatekNumPixels)
|
||||
}
|
||||
|
||||
// Send packets if the pixel values have changed.
|
||||
if controller.nearStrip.shouldSendPacket() {
|
||||
controller.nearStrip.populatePacketPixels(controller.packet[advatekPixelDataOffset:])
|
||||
controller.sendPacket(advatekNearStripUniverse)
|
||||
}
|
||||
if controller.farStrip.shouldSendPacket() {
|
||||
controller.farStrip.populatePacketPixels(controller.packet[advatekPixelDataOffset:])
|
||||
controller.sendPacket(advatekFarStripUniverse)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 createBlankAdvatekPacket(numPixels int) []byte {
|
||||
size := advatekPixelDataOffset + 3*numPixels
|
||||
packet := make([]byte, size)
|
||||
|
||||
// Preamble size
|
||||
packet[0] = 0x00
|
||||
packet[1] = 0x10
|
||||
|
||||
// Postamble size
|
||||
packet[2] = 0x00
|
||||
packet[3] = 0x00
|
||||
|
||||
// ACN packet identifier
|
||||
packet[4] = 0x41
|
||||
packet[5] = 0x53
|
||||
packet[6] = 0x43
|
||||
packet[7] = 0x2d
|
||||
packet[8] = 0x45
|
||||
packet[9] = 0x31
|
||||
packet[10] = 0x2e
|
||||
packet[11] = 0x31
|
||||
packet[12] = 0x37
|
||||
packet[13] = 0x00
|
||||
packet[14] = 0x00
|
||||
packet[15] = 0x00
|
||||
|
||||
// Root PDU length and flags
|
||||
rootPduLength := size - 16
|
||||
packet[16] = 0x70 | byte(rootPduLength>>8)
|
||||
packet[17] = byte(rootPduLength & 0xff)
|
||||
|
||||
// E1.31 vector indicating that this is a data packet
|
||||
packet[18] = 0x00
|
||||
packet[19] = 0x00
|
||||
packet[20] = 0x00
|
||||
packet[21] = 0x04
|
||||
|
||||
// Component ID
|
||||
for i, b := range []byte(advatekSourceName) {
|
||||
packet[22+i] = b
|
||||
}
|
||||
|
||||
// Framing PDU length and flags
|
||||
framingPduLength := size - 38
|
||||
packet[38] = 0x70 | byte(framingPduLength>>8)
|
||||
packet[39] = byte(framingPduLength & 0xff)
|
||||
|
||||
// E1.31 vector indicating that this is a data packet
|
||||
packet[40] = 0x00
|
||||
packet[41] = 0x00
|
||||
packet[42] = 0x00
|
||||
packet[43] = 0x02
|
||||
|
||||
// Source name
|
||||
for i, b := range []byte(advatekSourceName) {
|
||||
packet[44+i] = b
|
||||
}
|
||||
|
||||
// Priority
|
||||
packet[108] = 100
|
||||
|
||||
// Universe for synchronization packets
|
||||
packet[109] = 0x00
|
||||
packet[110] = 0x00
|
||||
|
||||
// Sequence number (initial value; will be updated whenever packet is sent)
|
||||
packet[111] = 0x00
|
||||
|
||||
// Options flags
|
||||
packet[112] = 0x00
|
||||
|
||||
// DMX universe (will be populated whenever packet is sent)
|
||||
packet[113] = 0x00
|
||||
packet[114] = 0x00
|
||||
|
||||
// DMP layer PDU length
|
||||
dmpPduLength := size - 115
|
||||
packet[115] = 0x70 | byte(dmpPduLength>>8)
|
||||
packet[116] = byte(dmpPduLength & 0xff)
|
||||
|
||||
// E1.31 vector indicating set property
|
||||
packet[117] = 0x02
|
||||
|
||||
// Address and data type
|
||||
packet[118] = 0xa1
|
||||
|
||||
// First property address
|
||||
packet[119] = 0x00
|
||||
packet[120] = 0x00
|
||||
|
||||
// Address increment
|
||||
packet[121] = 0x00
|
||||
packet[122] = 0x01
|
||||
|
||||
// Property value count
|
||||
count := 1 + 3*numPixels
|
||||
packet[123] = byte(count >> 8)
|
||||
packet[124] = byte(count & 0xff)
|
||||
|
||||
// DMX start code
|
||||
packet[125] = 0
|
||||
|
||||
// Remainder of packet is pixel data which will be populated whenever packet is sent.
|
||||
return packet
|
||||
}
|
||||
|
||||
func (controller *AdvatekController) sendPacket(dmxUniverse int) error {
|
||||
// Update non-static packet fields.
|
||||
controller.packet[111]++
|
||||
controller.packet[113] = byte(dmxUniverse >> 8)
|
||||
controller.packet[114] = byte(dmxUniverse & 0xff)
|
||||
|
||||
_, err := controller.conn.Write(controller.packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
// Copyright 2018 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Represents an independently controlled LED strip.
|
||||
|
||||
package led
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type advatekStrip struct {
|
||||
currentMode StripMode
|
||||
isRed bool
|
||||
pixels [advatekNumPixels][3]byte
|
||||
oldPixels [advatekNumPixels][3]byte
|
||||
counter int
|
||||
lastPacketTime time.Time
|
||||
}
|
||||
|
||||
// Calculates the current pixel values depending on the mode and elapsed counter cycles.
|
||||
func (strip *advatekStrip) updatePixels() {
|
||||
switch strip.currentMode {
|
||||
case RedMode:
|
||||
strip.updateSingleColorMode(Red)
|
||||
case GreenMode:
|
||||
strip.updateSingleColorMode(Green)
|
||||
case BlueMode:
|
||||
strip.updateSingleColorMode(Blue)
|
||||
case WhiteMode:
|
||||
strip.updateSingleColorMode(White)
|
||||
case PurpleMode:
|
||||
strip.updateSingleColorMode(Purple)
|
||||
case ChaseMode:
|
||||
strip.updateChaseMode()
|
||||
case RandomMode:
|
||||
strip.updateRandomMode()
|
||||
case FadeRedBlueMode:
|
||||
strip.updateFadeRedBlueMode()
|
||||
case FadeSingleMode:
|
||||
strip.updateFadeSingleMode()
|
||||
case GradientMode:
|
||||
strip.updateGradientMode()
|
||||
case BlinkMode:
|
||||
strip.updateBlinkMode()
|
||||
default:
|
||||
strip.updateOffMode()
|
||||
}
|
||||
strip.counter++
|
||||
}
|
||||
|
||||
// Returns true if the pixel data has changed or it has been too long since the last packet was sent.
|
||||
func (strip *advatekStrip) shouldSendPacket() bool {
|
||||
for i := 0; i < advatekNumPixels; i++ {
|
||||
if strip.pixels[i] != strip.oldPixels[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return time.Since(strip.lastPacketTime).Seconds() > advatekPacketTimeoutSec
|
||||
}
|
||||
|
||||
// Writes the pixel RGB values into the given packet in preparation for sending.
|
||||
func (strip *advatekStrip) populatePacketPixels(pixelData []byte) {
|
||||
for i, pixel := range strip.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.
|
||||
strip.oldPixels = strip.pixels
|
||||
strip.lastPacketTime = time.Now()
|
||||
}
|
||||
|
||||
// Returns the primary color (red or blue) of this strip.
|
||||
func (strip *advatekStrip) getColor() Color {
|
||||
if strip.isRed {
|
||||
return Red
|
||||
}
|
||||
return Blue
|
||||
}
|
||||
|
||||
// Returns the opposite primary color (red or blue) to this strip.
|
||||
func (strip *advatekStrip) getOppositeColor() Color {
|
||||
if strip.isRed {
|
||||
return Blue
|
||||
}
|
||||
return Red
|
||||
}
|
||||
|
||||
// Returns a color partway between purple and the primary color (red or blue) of this strip.
|
||||
func (strip *advatekStrip) getMidColor() Color {
|
||||
if strip.isRed {
|
||||
return PurpleBlue
|
||||
}
|
||||
return PurpleRed
|
||||
}
|
||||
|
||||
// Returns a dim version of the primary color (red or blue) of this strip.
|
||||
func (strip *advatekStrip) getDimColor() Color {
|
||||
if strip.isRed {
|
||||
return DimRed
|
||||
}
|
||||
return DimBlue
|
||||
}
|
||||
|
||||
// Returns a dim version of the opposite primary color (red or blue) of this strip.
|
||||
func (strip *advatekStrip) getDimOppositeColor() Color {
|
||||
if strip.isRed {
|
||||
return DimBlue
|
||||
}
|
||||
return DimRed
|
||||
}
|
||||
|
||||
// Returns the starting offset for the gradient mode for this strip.
|
||||
func (strip *advatekStrip) getGradientStartOffset() int {
|
||||
if strip.isRed {
|
||||
return advatekNumPixels / 3
|
||||
}
|
||||
return 2 * advatekNumPixels / 3
|
||||
}
|
||||
|
||||
func (strip *advatekStrip) updateOffMode() {
|
||||
for i := 0; i < advatekNumPixels; i++ {
|
||||
strip.pixels[i] = Colors[Black]
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *advatekStrip) updateSingleColorMode(color Color) {
|
||||
for i := 0; i < advatekNumPixels; i++ {
|
||||
strip.pixels[i] = Colors[color]
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *advatekStrip) updateChaseMode() {
|
||||
if strip.counter == int(Black)*advatekNumPixels { // Ignore colors listed after white.
|
||||
strip.counter = 0
|
||||
}
|
||||
color := Color(strip.counter / advatekNumPixels)
|
||||
pixelIndex := strip.counter % advatekNumPixels
|
||||
strip.pixels[pixelIndex] = Colors[color]
|
||||
}
|
||||
|
||||
func (strip *advatekStrip) updateRandomMode() {
|
||||
if strip.counter%10 != 0 {
|
||||
return
|
||||
}
|
||||
for i := 0; i < advatekNumPixels; i++ {
|
||||
strip.pixels[i] = Colors[Color(rand.Intn(int(Black)))] // Ignore colors listed after white.
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *advatekStrip) updateFadeRedBlueMode() {
|
||||
fadeCycles := 40
|
||||
holdCycles := 10
|
||||
if strip.counter == 4*holdCycles+4*fadeCycles {
|
||||
strip.counter = 0
|
||||
}
|
||||
|
||||
for i := 0; i < advatekNumPixels; i++ {
|
||||
if strip.counter < holdCycles {
|
||||
strip.pixels[i] = Colors[Black]
|
||||
} else if 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]
|
||||
} else if strip.counter < 2*holdCycles+2*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]
|
||||
} else if strip.counter < 3*holdCycles+3*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]
|
||||
} else if strip.counter < 4*holdCycles+4*fadeCycles {
|
||||
strip.pixels[i] = getFadeColor(Blue, Black, strip.counter-4*holdCycles-3*fadeCycles, fadeCycles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *advatekStrip) updateFadeSingleMode() {
|
||||
offCycles := 50
|
||||
fadeCycles := 100
|
||||
if strip.counter == offCycles+2*fadeCycles {
|
||||
strip.counter = 0
|
||||
}
|
||||
|
||||
for i := 0; i < advatekNumPixels; i++ {
|
||||
if strip.counter < offCycles {
|
||||
strip.pixels[i] = Colors[Black]
|
||||
} else if 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *advatekStrip) updateGradientMode() {
|
||||
for i := 0; i < advatekNumPixels; i++ {
|
||||
strip.pixels[advatekNumPixels-i-1] = getGradientColor(i+strip.counter, 75)
|
||||
}
|
||||
}
|
||||
|
||||
func (strip *advatekStrip) updateBlinkMode() {
|
||||
divisor := 10
|
||||
for i := 0; i < advatekNumPixels; i++ {
|
||||
if strip.counter%divisor < divisor/2 {
|
||||
strip.pixels[i] = Colors[White]
|
||||
} else {
|
||||
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]
|
||||
var fadeColor [3]byte
|
||||
for i := 0; i < 3; i++ {
|
||||
fadeColor[i] = byte(int(from[i]) + numerator*(int(to[i])-int(from[i]))/denominator)
|
||||
}
|
||||
return fadeColor
|
||||
}
|
||||
|
||||
// Calculates the value of a single pixel in a gradient.
|
||||
func getGradientColor(offset, numPixels int) [3]byte {
|
||||
offset %= numPixels
|
||||
if 3*offset < numPixels {
|
||||
return getFadeColor(Red, Green, 3*offset, numPixels)
|
||||
} else if 3*offset < 2*numPixels {
|
||||
return getFadeColor(Green, Blue, 3*offset-numPixels, numPixels)
|
||||
} else {
|
||||
return getFadeColor(Blue, Red, 3*offset-2*numPixels, numPixels)
|
||||
}
|
||||
}
|
||||
40
led/color.go
40
led/color.go
@@ -1,40 +0,0 @@
|
||||
// Copyright 2018 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Contains pixel RGB mappings for common colors.
|
||||
|
||||
package led
|
||||
|
||||
type Color int
|
||||
|
||||
const (
|
||||
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},
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
// 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 led
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
colorKineticsPort = 6038
|
||||
colorKineticsPacketTimeoutSec = 1
|
||||
colorKineticsNumPixels = 17
|
||||
colorKineticsPixelDataOffset = 21
|
||||
)
|
||||
|
||||
type ColorKineticsController struct {
|
||||
CurrentForceMode VaultMode
|
||||
CurrentLevitateMode VaultMode
|
||||
CurrentBoostMode VaultMode
|
||||
pixels [colorKineticsNumPixels][3]byte
|
||||
oldPixels [colorKineticsNumPixels][3]byte
|
||||
conn net.Conn
|
||||
packet []byte
|
||||
lastPacketTime time.Time
|
||||
}
|
||||
|
||||
func (controller *ColorKineticsController) 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, colorKineticsPort)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the current mode for the section of LEDs corresponding to the force powerup.
|
||||
func (controller *ColorKineticsController) SetForceMode(mode VaultMode) {
|
||||
controller.CurrentForceMode = mode
|
||||
controller.setPixels(0, mode)
|
||||
}
|
||||
|
||||
// Sets the current mode for the section of LEDs corresponding to the levitate powerup.
|
||||
func (controller *ColorKineticsController) SetLevitateMode(mode VaultMode) {
|
||||
controller.CurrentLevitateMode = mode
|
||||
controller.setPixels(6, mode)
|
||||
}
|
||||
|
||||
// Sets the current mode for the section of LEDs corresponding to the boost powerup.
|
||||
func (controller *ColorKineticsController) SetBoostMode(mode VaultMode) {
|
||||
controller.CurrentBoostMode = mode
|
||||
controller.setPixels(12, mode)
|
||||
}
|
||||
|
||||
// Sets the current mode for all sections of LEDs to the same value.
|
||||
func (controller *ColorKineticsController) SetAllModes(mode VaultMode) {
|
||||
controller.SetForceMode(mode)
|
||||
controller.SetLevitateMode(mode)
|
||||
controller.SetBoostMode(mode)
|
||||
}
|
||||
|
||||
// Sends a packet if necessary. Should be called from a timed loop.
|
||||
func (controller *ColorKineticsController) 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 = createBlankColorKineticsPacket(colorKineticsPort)
|
||||
}
|
||||
|
||||
// Send packets if the pixel values have changed.
|
||||
if controller.shouldSendPacket() {
|
||||
controller.populatePacketPixels(controller.packet[colorKineticsPixelDataOffset:])
|
||||
controller.sendPacket()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (controller *ColorKineticsController) setPixels(offset int, mode VaultMode) {
|
||||
for i := 0; i < 5; i++ {
|
||||
controller.pixels[offset+i] = Colors[Black]
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case ThreeCubeMode:
|
||||
controller.pixels[offset+3] = Colors[Yellow]
|
||||
fallthrough
|
||||
case TwoCubeMode:
|
||||
controller.pixels[offset+2] = Colors[Yellow]
|
||||
fallthrough
|
||||
case OneCubeMode:
|
||||
controller.pixels[offset+1] = Colors[Yellow]
|
||||
case RedPlayedMode:
|
||||
for i := 0; i < 5; i++ {
|
||||
controller.pixels[offset+i] = Colors[Red]
|
||||
}
|
||||
case BluePlayedMode:
|
||||
for i := 0; i < 5; i++ {
|
||||
controller.pixels[offset+i] = Colors[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 createBlankColorKineticsPacket(numPixels int) []byte {
|
||||
size := colorKineticsPixelDataOffset + 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 *ColorKineticsController) shouldSendPacket() bool {
|
||||
for i := 0; i < colorKineticsNumPixels; i++ {
|
||||
if controller.pixels[i] != controller.oldPixels[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return time.Since(controller.lastPacketTime).Seconds() > colorKineticsPacketTimeoutSec
|
||||
}
|
||||
|
||||
// Writes the pixel RGB values into the given packet in preparation for sending.
|
||||
func (controller *ColorKineticsController) 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 *ColorKineticsController) sendPacket() error {
|
||||
_, err := controller.conn.Write(controller.packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2018 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Contains the set of display modes for an LED strip.
|
||||
|
||||
package led
|
||||
|
||||
type StripMode int
|
||||
|
||||
const (
|
||||
OffMode StripMode = iota
|
||||
RedMode
|
||||
GreenMode
|
||||
BlueMode
|
||||
WhiteMode
|
||||
PurpleMode
|
||||
ChaseMode
|
||||
RandomMode
|
||||
FadeRedBlueMode
|
||||
FadeSingleMode
|
||||
GradientMode
|
||||
BlinkMode
|
||||
)
|
||||
|
||||
var StripModeNames = map[StripMode]string{
|
||||
OffMode: "Off",
|
||||
RedMode: "Red",
|
||||
GreenMode: "Green",
|
||||
BlueMode: "Blue",
|
||||
WhiteMode: "White",
|
||||
PurpleMode: "Purple",
|
||||
ChaseMode: "Chase",
|
||||
RandomMode: "Random",
|
||||
FadeRedBlueMode: "Fade Red/Blue",
|
||||
FadeSingleMode: "Fade Single",
|
||||
GradientMode: "Gradient",
|
||||
BlinkMode: "Blink",
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2018 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Contains the set of display modes for the vault LEDs.
|
||||
|
||||
package led
|
||||
|
||||
type VaultMode int
|
||||
|
||||
const (
|
||||
VaultOffMode VaultMode = iota
|
||||
OneCubeMode
|
||||
TwoCubeMode
|
||||
ThreeCubeMode
|
||||
RedPlayedMode
|
||||
BluePlayedMode
|
||||
)
|
||||
|
||||
var VaultModeNames = map[VaultMode]string{
|
||||
VaultOffMode: "Off",
|
||||
OneCubeMode: "One Cube",
|
||||
TwoCubeMode: "Two Cubes",
|
||||
ThreeCubeMode: "Three Cubes",
|
||||
RedPlayedMode: "Red Played",
|
||||
BluePlayedMode: "Blue Played",
|
||||
}
|
||||
@@ -5,21 +5,6 @@
|
||||
|
||||
var websocket;
|
||||
|
||||
// Sends a websocket message to change the LED display mode.
|
||||
var setLedMode = function() {
|
||||
websocket.send("setLedMode", {LedMode: parseInt($("input[name=ledMode]:checked").val()),
|
||||
VaultLedMode: parseInt($("input[name=vaultLedMode]:checked").val())});
|
||||
};
|
||||
|
||||
// Handles a websocket message to update the LED test mode.
|
||||
var handleLedMode = function(data) {
|
||||
$("input[name=ledMode]:checked").prop("checked", false);
|
||||
$("input[name=ledMode][value=" + data.LedMode + "]").prop("checked", true);
|
||||
|
||||
$("input[name=vaultLedMode]:checked").prop("checked", false);
|
||||
$("input[name=vaultLedMode][value=" + data.VaultLedMode + "]").prop("checked", true);
|
||||
};
|
||||
|
||||
// Handles a websocket message to update the PLC IO status.
|
||||
var handlePlcIoChange = function(data) {
|
||||
$.each(data.Inputs, function(index, input) {
|
||||
@@ -40,7 +25,6 @@ var handlePlcIoChange = function(data) {
|
||||
$(function() {
|
||||
// Set up the websocket back to the server.
|
||||
websocket = new CheesyWebsocket("/setup/led_plc/websocket", {
|
||||
ledMode: function(event) {handleLedMode(event.data); },
|
||||
plcIoChange: function(event) { handlePlcIoChange(event.data); }
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,14 +6,8 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/Team254/cheesy-arena/led"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/websocket"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -31,13 +25,10 @@ func (web *Web) ledPlcGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
plc := web.arena.Plc
|
||||
data := struct {
|
||||
*model.EventSettings
|
||||
InputNames []string
|
||||
RegisterNames []string
|
||||
CoilNames []string
|
||||
LedModeNames map[led.StripMode]string
|
||||
VaultLedModeNames map[led.VaultMode]string
|
||||
}{web.arena.EventSettings, plc.GetInputNames(), plc.GetRegisterNames(), plc.GetCoilNames(), led.StripModeNames,
|
||||
led.VaultModeNames}
|
||||
InputNames []string
|
||||
RegisterNames []string
|
||||
CoilNames []string
|
||||
}{web.arena.EventSettings, plc.GetInputNames(), plc.GetRegisterNames(), plc.GetCoilNames()}
|
||||
err = template.ExecuteTemplate(w, "base", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
@@ -59,35 +50,5 @@ func (web *Web) ledPlcWebsocketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer ws.Close()
|
||||
|
||||
// Subscribe the websocket to the notifiers whose messages will be passed on to the client, in a separate goroutine.
|
||||
go ws.HandleNotifiers(web.arena.LedModeNotifier, web.arena.Plc.IoChangeNotifier)
|
||||
|
||||
// Loop, waiting for commands and responding to them, until the client closes the connection.
|
||||
for {
|
||||
messageType, data, err := ws.Read()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
// Client has closed the connection; nothing to do here.
|
||||
return
|
||||
}
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
switch messageType {
|
||||
case "setLedMode":
|
||||
if web.arena.MatchState != field.PreMatch && web.arena.MatchState != field.TimeoutActive &&
|
||||
web.arena.MatchState != field.PostTimeout {
|
||||
ws.WriteError("Arena must be in pre-match state")
|
||||
continue
|
||||
}
|
||||
var modeMessage field.LedModeMessage
|
||||
err = mapstructure.Decode(data, &modeMessage)
|
||||
if err != nil {
|
||||
ws.WriteError(err.Error())
|
||||
continue
|
||||
}
|
||||
default:
|
||||
ws.WriteError(fmt.Sprintf("Invalid message type '%s'.", messageType))
|
||||
}
|
||||
}
|
||||
ws.HandleNotifiers(web.arena.Plc.IoChangeNotifier)
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@ import (
|
||||
gorillawebsocket "github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func TestSetupLedPlcWebsocket(t *testing.T) {
|
||||
@@ -24,14 +21,5 @@ func TestSetupLedPlcWebsocket(t *testing.T) {
|
||||
ws := websocket.NewTestWebsocket(conn)
|
||||
|
||||
// Should get a few status updates right after connection.
|
||||
readLedModes(t, ws)
|
||||
readWebsocketType(t, ws, "plcIoChange")
|
||||
}
|
||||
|
||||
func readLedModes(t *testing.T, ws *websocket.Websocket) *field.LedModeMessage {
|
||||
message := readWebsocketType(t, ws, "ledMode")
|
||||
var ledModeMessage field.LedModeMessage
|
||||
err := mapstructure.Decode(message, &ledModeMessage)
|
||||
assert.Nil(t, err)
|
||||
return &ledModeMessage
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user