mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Add initial LED implementation for an E1.31 DMX over Ethernet controller.
This commit is contained in:
@@ -8,6 +8,7 @@ package field
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Team254/cheesy-arena/game"
|
"github.com/Team254/cheesy-arena/game"
|
||||||
|
"github.com/Team254/cheesy-arena/led"
|
||||||
"github.com/Team254/cheesy-arena/model"
|
"github.com/Team254/cheesy-arena/model"
|
||||||
"github.com/Team254/cheesy-arena/partner"
|
"github.com/Team254/cheesy-arena/partner"
|
||||||
"log"
|
"log"
|
||||||
@@ -56,7 +57,6 @@ type Arena struct {
|
|||||||
AllianceStationDisplays map[string]string
|
AllianceStationDisplays map[string]string
|
||||||
AllianceStationDisplayScreen string
|
AllianceStationDisplayScreen string
|
||||||
MuteMatchSounds bool
|
MuteMatchSounds bool
|
||||||
FieldTestMode string
|
|
||||||
matchAborted bool
|
matchAborted bool
|
||||||
matchStateNotifier *Notifier
|
matchStateNotifier *Notifier
|
||||||
MatchTimeNotifier *Notifier
|
MatchTimeNotifier *Notifier
|
||||||
@@ -71,6 +71,7 @@ type Arena struct {
|
|||||||
AllianceSelectionNotifier *Notifier
|
AllianceSelectionNotifier *Notifier
|
||||||
LowerThirdNotifier *Notifier
|
LowerThirdNotifier *Notifier
|
||||||
ReloadDisplaysNotifier *Notifier
|
ReloadDisplaysNotifier *Notifier
|
||||||
|
RedSwitchLedStrip *led.LedStrip
|
||||||
scale *game.Seesaw
|
scale *game.Seesaw
|
||||||
redSwitch *game.Seesaw
|
redSwitch *game.Seesaw
|
||||||
blueSwitch *game.Seesaw
|
blueSwitch *game.Seesaw
|
||||||
@@ -143,6 +144,12 @@ func NewArena(dbPath string) (*Arena, error) {
|
|||||||
arena.AllianceStationDisplays = make(map[string]string)
|
arena.AllianceStationDisplays = make(map[string]string)
|
||||||
arena.AllianceStationDisplayScreen = "match"
|
arena.AllianceStationDisplayScreen = "match"
|
||||||
|
|
||||||
|
// Initialize LEDs.
|
||||||
|
arena.RedSwitchLedStrip, err = led.NewLedStrip("10.0.0.30", 1, 150)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return arena, nil
|
return arena, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,8 +381,8 @@ func (arena *Arena) Update() {
|
|||||||
enabled = false
|
enabled = false
|
||||||
arena.AudienceDisplayScreen = "match"
|
arena.AudienceDisplayScreen = "match"
|
||||||
arena.AudienceDisplayNotifier.Notify(nil)
|
arena.AudienceDisplayNotifier.Notify(nil)
|
||||||
arena.FieldTestMode = ""
|
|
||||||
arena.sendGameSpecificDataPacket()
|
arena.sendGameSpecificDataPacket()
|
||||||
|
arena.RedSwitchLedStrip.SetMode(led.WarmupMode)
|
||||||
if !arena.MuteMatchSounds {
|
if !arena.MuteMatchSounds {
|
||||||
arena.PlaySoundNotifier.Notify("match-warmup")
|
arena.PlaySoundNotifier.Notify("match-warmup")
|
||||||
}
|
}
|
||||||
@@ -471,6 +478,8 @@ func (arena *Arena) Update() {
|
|||||||
// Handle field sensors/lights/motors.
|
// Handle field sensors/lights/motors.
|
||||||
arena.handlePlcInput()
|
arena.handlePlcInput()
|
||||||
arena.handlePlcOutput()
|
arena.handlePlcOutput()
|
||||||
|
|
||||||
|
arena.RedSwitchLedStrip.Update()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loops indefinitely to track and update the arena components.
|
// Loops indefinitely to track and update the arena components.
|
||||||
@@ -694,30 +703,7 @@ func (arena *Arena) handlePlcInput() {
|
|||||||
|
|
||||||
// Writes light/motor commands to the field PLC.
|
// Writes light/motor commands to the field PLC.
|
||||||
func (arena *Arena) handlePlcOutput() {
|
func (arena *Arena) handlePlcOutput() {
|
||||||
if arena.FieldTestMode != "" {
|
// TODO(patrick): Update for 2018.
|
||||||
// PLC output is being manually overridden.
|
|
||||||
// TODO(patrick): Update for 2018.
|
|
||||||
/*
|
|
||||||
if arena.FieldTestMode == "flash" {
|
|
||||||
blinkState := arena.Plc.GetCycleState(2, 0, 1)
|
|
||||||
arena.Plc.SetTouchpadLights([3]bool{blinkState, blinkState, blinkState},
|
|
||||||
[3]bool{blinkState, blinkState, blinkState})
|
|
||||||
} else if arena.FieldTestMode == "cycle" {
|
|
||||||
arena.Plc.SetTouchpadLights(
|
|
||||||
[3]bool{arena.Plc.GetCycleState(3, 2, 1), arena.Plc.GetCycleState(3, 1, 1), arena.Plc.GetCycleState(3, 0, 1)},
|
|
||||||
[3]bool{arena.Plc.GetCycleState(3, 0, 1), arena.Plc.GetCycleState(3, 1, 1), arena.Plc.GetCycleState(3, 2, 1)})
|
|
||||||
} else if arena.FieldTestMode == "chase" {
|
|
||||||
arena.Plc.SetTouchpadLights(
|
|
||||||
[3]bool{arena.Plc.GetCycleState(12, 2, 2), arena.Plc.GetCycleState(12, 1, 2), arena.Plc.GetCycleState(12, 0, 2)},
|
|
||||||
[3]bool{arena.Plc.GetCycleState(12, 3, 2), arena.Plc.GetCycleState(12, 4, 2), arena.Plc.GetCycleState(12, 5, 2)})
|
|
||||||
} else if arena.FieldTestMode == "slowChase" {
|
|
||||||
arena.Plc.SetTouchpadLights(
|
|
||||||
[3]bool{arena.Plc.GetCycleState(6, 2, 8), arena.Plc.GetCycleState(6, 1, 8), arena.Plc.GetCycleState(6, 0, 8)},
|
|
||||||
[3]bool{arena.Plc.GetCycleState(6, 3, 8), arena.Plc.GetCycleState(6, 4, 8), arena.Plc.GetCycleState(6, 5, 8)})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (arena *Arena) handleEstop(station string, state bool) {
|
func (arena *Arena) handleEstop(station string, state bool) {
|
||||||
|
|||||||
343
led/led_strip.go
Normal file
343
led/led_strip.go
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
//
|
||||||
|
// Represents a LED strip attached to an E1.31 sACN (DMX over Ethernet) controller.
|
||||||
|
|
||||||
|
package led
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
controllerPort = 5568
|
||||||
|
sourceName = "Cheesy Arena"
|
||||||
|
packetTimeoutSec = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// LED sequence modes
|
||||||
|
const (
|
||||||
|
OffMode = iota
|
||||||
|
RedMode
|
||||||
|
GreenMode
|
||||||
|
BlueMode
|
||||||
|
WhiteMode
|
||||||
|
ChaseMode
|
||||||
|
WarmupMode
|
||||||
|
RandomMode
|
||||||
|
FadeMode
|
||||||
|
)
|
||||||
|
|
||||||
|
// Color RGB mappings
|
||||||
|
const (
|
||||||
|
red = iota
|
||||||
|
orange
|
||||||
|
yellow
|
||||||
|
green
|
||||||
|
teal
|
||||||
|
blue
|
||||||
|
purple
|
||||||
|
white
|
||||||
|
black
|
||||||
|
)
|
||||||
|
var colors = [][3]byte{
|
||||||
|
{255, 0, 0}, // Red
|
||||||
|
{255, 50, 0}, // Orange
|
||||||
|
{255, 255, 0}, // Yellow
|
||||||
|
{0, 255, 0}, // Green
|
||||||
|
{0, 100, 100}, // Teal
|
||||||
|
{0, 0, 255}, // Blue
|
||||||
|
{100, 0, 100}, // Purple
|
||||||
|
{255, 255, 255}, // White
|
||||||
|
{0, 0, 0}, // Black
|
||||||
|
}
|
||||||
|
|
||||||
|
type LedStrip struct {
|
||||||
|
Mode int
|
||||||
|
conn net.Conn
|
||||||
|
pixels [][3]byte
|
||||||
|
oldPixels [][3]byte
|
||||||
|
packet []byte
|
||||||
|
counter int
|
||||||
|
lastPacketTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLedStrip(controllerAddress string, dmxUniverse int, numPixels int) (*LedStrip, error) {
|
||||||
|
ledStrip := new(LedStrip)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ledStrip.conn, err = net.Dial("udp4", fmt.Sprintf("%s:%d", controllerAddress, controllerPort))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ledStrip.pixels = make([][3]byte, numPixels)
|
||||||
|
ledStrip.oldPixels = make([][3]byte, numPixels)
|
||||||
|
ledStrip.packet = createBlankPacket(dmxUniverse, numPixels)
|
||||||
|
|
||||||
|
return ledStrip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the current LED sequence mode and resets the intra-sequence counter to the beginning.
|
||||||
|
func (strip *LedStrip) SetMode(mode int) {
|
||||||
|
strip.Mode = mode
|
||||||
|
strip.counter = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advances the pixel values through the current sequence and sends a packet if necessary. Should be called from a timed
|
||||||
|
// loop.
|
||||||
|
func (strip *LedStrip) Update() error {
|
||||||
|
// Determine the pixel values.
|
||||||
|
switch strip.Mode {
|
||||||
|
case RedMode:
|
||||||
|
strip.updateSingleColorMode(red)
|
||||||
|
case GreenMode:
|
||||||
|
strip.updateSingleColorMode(green)
|
||||||
|
case BlueMode:
|
||||||
|
strip.updateSingleColorMode(blue)
|
||||||
|
case WhiteMode:
|
||||||
|
strip.updateSingleColorMode(white)
|
||||||
|
case ChaseMode:
|
||||||
|
strip.updateChaseMode()
|
||||||
|
case WarmupMode:
|
||||||
|
strip.updateWarmupMode()
|
||||||
|
case RandomMode:
|
||||||
|
strip.updateRandomMode()
|
||||||
|
case FadeMode:
|
||||||
|
strip.updateFadeMode()
|
||||||
|
default:
|
||||||
|
strip.updateOffMode()
|
||||||
|
}
|
||||||
|
strip.counter++
|
||||||
|
|
||||||
|
// Update non-static packet fields.
|
||||||
|
strip.packet[111]++
|
||||||
|
for i, pixel := range strip.pixels {
|
||||||
|
strip.packet[126+3*i] = pixel[0]
|
||||||
|
strip.packet[127+3*i] = pixel[1]
|
||||||
|
strip.packet[128+3*i] = pixel[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the packet if it hasn't changed.
|
||||||
|
if strip.shouldSendPacket() {
|
||||||
|
_, err := strip.conn.Write(strip.packet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
strip.lastPacketTime = time.Now()
|
||||||
|
|
||||||
|
// Keep a record of the pixel values in order to detect future changes.
|
||||||
|
copy(strip.oldPixels, strip.pixels)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 createBlankPacket(dmxUniverse, numPixels int) []byte {
|
||||||
|
size := 126 + 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(sourceName) {
|
||||||
|
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(sourceName) {
|
||||||
|
packet[44+i] = b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority
|
||||||
|
packet[108] = 100
|
||||||
|
|
||||||
|
// Universe for synchronization packets
|
||||||
|
packet[109] = 0x00
|
||||||
|
packet[110] = 0x00
|
||||||
|
|
||||||
|
// Sequence number (initial value)
|
||||||
|
packet[111] = 0x00
|
||||||
|
|
||||||
|
// Options flags
|
||||||
|
packet[112] = 0x00
|
||||||
|
|
||||||
|
// Universe
|
||||||
|
packet[113] = byte(dmxUniverse >> 8)
|
||||||
|
packet[114] = byte(dmxUniverse & 0xff)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
return packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the pixel data has changed or it has been too long since the last packet was sent.
|
||||||
|
func (strip *LedStrip) shouldSendPacket() bool {
|
||||||
|
for i := 0; i < len(strip.pixels); i++ {
|
||||||
|
if strip.pixels[i] != strip.oldPixels[i] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Since(strip.lastPacketTime).Seconds() > packetTimeoutSec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (strip *LedStrip) updateOffMode() {
|
||||||
|
for i := 0; i < len(strip.pixels); i++ {
|
||||||
|
strip.pixels[i] = colors[black]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (strip *LedStrip) updateSingleColorMode(color int) {
|
||||||
|
for i := 0; i < len(strip.pixels); i++ {
|
||||||
|
strip.pixels[i] = colors[color]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (strip *LedStrip) updateChaseMode() {
|
||||||
|
if strip.counter == len(colors)*len(strip.pixels) {
|
||||||
|
strip.counter = 0
|
||||||
|
}
|
||||||
|
color := strip.counter / len(strip.pixels)
|
||||||
|
pixelIndex := strip.counter % len(strip.pixels)
|
||||||
|
strip.pixels[pixelIndex] = colors[color]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (strip *LedStrip) updateWarmupMode() {
|
||||||
|
endCounter := 250
|
||||||
|
if strip.counter == 0 {
|
||||||
|
// Show solid white to start.
|
||||||
|
for i := 0; i < len(strip.pixels); i++ {
|
||||||
|
strip.pixels[i] = colors[white]
|
||||||
|
}
|
||||||
|
} else if strip.counter <= endCounter {
|
||||||
|
// Build to the alliance color from each side.
|
||||||
|
numLitPixels := len(strip.pixels) / 2 * strip.counter / endCounter
|
||||||
|
for i := 0; i < numLitPixels; i++ {
|
||||||
|
strip.pixels[i] = colors[red]
|
||||||
|
strip.pixels[len(strip.pixels)-i-1] = colors[red]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// MaintainPrevent the counter from rolling over.
|
||||||
|
strip.counter = endCounter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (strip *LedStrip) updateRandomMode() {
|
||||||
|
if strip.counter%10 != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < len(strip.pixels); i++ {
|
||||||
|
strip.pixels[i] = colors[rand.Intn(len(colors))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (strip *LedStrip) updateFadeMode() {
|
||||||
|
fadeCycles := 40
|
||||||
|
holdCycles := 10
|
||||||
|
if strip.counter == 4*holdCycles+4*fadeCycles {
|
||||||
|
strip.counter = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(strip.pixels); 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolates between the two colors based on the given fraction.
|
||||||
|
func getFadeColor(fromColor, toColor, 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
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
{{define "title"}}Field Configuration{{end}}
|
{{define "title"}}Field Configuration{{end}}
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4 col-lg-offset-2">
|
<div class="col-lg-4">
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<legend>Alliance Station Displays</legend>
|
<legend>Alliance Station Displays</legend>
|
||||||
{{range $displayId, $station := .AllianceStationDisplays}}
|
{{range $displayId, $station := .AllianceStationDisplays}}
|
||||||
@@ -38,82 +38,6 @@
|
|||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<legend>PLC</legend>
|
<legend>PLC</legend>
|
||||||
<form class="" action="/setup/field/test" method="POST">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode ""}}checked{{end}}>Off
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="boiler" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "boiler"}}checked{{end}}>Boilers On
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="rotor1" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "rotor1"}}checked{{end}}>1 Rotor/Touchpad 1 On
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="rotor2" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "rotor2"}}checked{{end}}>2 Rotors/Touchpad 2 On
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="rotor3" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "rotor3"}}checked{{end}}>3 Rotors/Touchpad 3 On
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="rotor4" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "rotor4"}}checked{{end}}>4 Rotors On
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="red" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "red"}}checked{{end}}>All Red On
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="blue" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "blue"}}checked{{end}}>All Blue On
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="flash" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "flash"}}checked{{end}}>Flash Touchpads
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="cycle" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "cycle"}}checked{{end}}>Cycle Touchpads
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="chase" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "chase"}}checked{{end}}>Chase Touchpads
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="radio">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="mode" value="slowChase" onclick="this.form.submit()"
|
|
||||||
{{if eq .FieldTestMode "slowChase"}}checked{{end}}>Slow Chase Touchpads
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<table>
|
<table>
|
||||||
@@ -157,6 +81,69 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="well">
|
||||||
|
<legend>LEDs</legend>
|
||||||
|
<form class="" action="/setup/field/test" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="0" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 0}}checked{{end}}>Off
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="1" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 1}}checked{{end}}>Red
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="2" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 2}}checked{{end}}>Green
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="3" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 3}}checked{{end}}>Blue
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="4" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 4}}checked{{end}}>White
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="5" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 5}}checked{{end}}>Chase
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="6" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 6}}checked{{end}}>Warmup
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="7" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 7}}checked{{end}}>Random
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="radio">
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="mode" value="8" onclick="this.form.submit()"
|
||||||
|
{{if eq .LedMode 8}}checked{{end}}>Fade
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{define "script"}}
|
{{define "script"}}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/Team254/cheesy-arena/field"
|
"github.com/Team254/cheesy-arena/field"
|
||||||
"github.com/Team254/cheesy-arena/model"
|
"github.com/Team254/cheesy-arena/model"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Shows the field configuration page.
|
// Shows the field configuration page.
|
||||||
@@ -25,12 +26,12 @@ func (web *Web) fieldGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
data := struct {
|
data := struct {
|
||||||
*model.EventSettings
|
*model.EventSettings
|
||||||
AllianceStationDisplays map[string]string
|
AllianceStationDisplays map[string]string
|
||||||
FieldTestMode string
|
|
||||||
Inputs []bool
|
Inputs []bool
|
||||||
Counters []uint16
|
Counters []uint16
|
||||||
Coils []bool
|
Coils []bool
|
||||||
}{web.arena.EventSettings, web.arena.AllianceStationDisplays, web.arena.FieldTestMode, web.arena.Plc.Inputs[:],
|
LedMode int
|
||||||
web.arena.Plc.Registers[:], web.arena.Plc.Coils[:]}
|
}{web.arena.EventSettings, web.arena.AllianceStationDisplays, web.arena.Plc.Inputs[:],
|
||||||
|
web.arena.Plc.Registers[:], web.arena.Plc.Coils[:], web.arena.RedSwitchLedStrip.Mode}
|
||||||
err = template.ExecuteTemplate(w, "base", data)
|
err = template.ExecuteTemplate(w, "base", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleWebErr(w, err)
|
handleWebErr(w, err)
|
||||||
@@ -72,53 +73,8 @@ func (web *Web) fieldTestPostHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(patrick): Update for 2018.
|
mode, _ := strconv.Atoi(r.PostFormValue("mode"))
|
||||||
mode := r.PostFormValue("mode")
|
web.arena.RedSwitchLedStrip.SetMode(mode)
|
||||||
/*
|
|
||||||
switch mode {
|
|
||||||
case "boiler":
|
|
||||||
web.arena.Plc.SetBoilerMotors(true)
|
|
||||||
web.arena.Plc.SetRotorMotors(0, 0)
|
|
||||||
web.arena.Plc.SetRotorLights(0, 0)
|
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
|
||||||
case "rotor1":
|
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
|
||||||
web.arena.Plc.SetRotorMotors(1, 1)
|
|
||||||
web.arena.Plc.SetRotorLights(1, 1)
|
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{true, false, false}, [3]bool{true, false, false})
|
|
||||||
case "rotor2":
|
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
|
||||||
web.arena.Plc.SetRotorMotors(2, 2)
|
|
||||||
web.arena.Plc.SetRotorLights(2, 2)
|
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, true, false}, [3]bool{false, true, false})
|
|
||||||
case "rotor3":
|
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
|
||||||
web.arena.Plc.SetRotorMotors(3, 3)
|
|
||||||
web.arena.Plc.SetRotorLights(2, 2)
|
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, true}, [3]bool{false, false, true})
|
|
||||||
case "rotor4":
|
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
|
||||||
web.arena.Plc.SetRotorMotors(4, 4)
|
|
||||||
web.arena.Plc.SetRotorLights(2, 2)
|
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
|
||||||
case "red":
|
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
|
||||||
web.arena.Plc.SetRotorMotors(4, 0)
|
|
||||||
web.arena.Plc.SetRotorLights(2, 0)
|
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{true, true, true}, [3]bool{false, false, false})
|
|
||||||
case "blue":
|
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
|
||||||
web.arena.Plc.SetRotorMotors(0, 4)
|
|
||||||
web.arena.Plc.SetRotorLights(0, 2)
|
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{true, true, true})
|
|
||||||
default:
|
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
|
||||||
web.arena.Plc.SetRotorMotors(0, 0)
|
|
||||||
web.arena.Plc.SetRotorLights(0, 0)
|
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
web.arena.FieldTestMode = mode
|
|
||||||
http.Redirect(w, r, "/setup/field", 303)
|
http.Redirect(w, r, "/setup/field", 303)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func TestSetupField(t *testing.T) {
|
|||||||
assert.Contains(t, recorder.Body.String(), "12345")
|
assert.Contains(t, recorder.Body.String(), "12345")
|
||||||
assert.Contains(t, recorder.Body.String(), "selected")
|
assert.Contains(t, recorder.Body.String(), "selected")
|
||||||
|
|
||||||
recorder = web.postHttpResponse("/setup/field/test", "mode=rotor2")
|
recorder = web.postHttpResponse("/setup/field/test", "mode=1")
|
||||||
assert.Equal(t, 303, recorder.Code)
|
assert.Equal(t, 303, recorder.Code)
|
||||||
assert.Equal(t, "rotor2", web.arena.FieldTestMode)
|
assert.Equal(t, 1, web.arena.RedSwitchLedStrip.Mode)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user