diff --git a/field/arena.go b/field/arena.go index 087dd53..791fded 100644 --- a/field/arena.go +++ b/field/arena.go @@ -12,6 +12,7 @@ import ( "github.com/Team254/cheesy-arena/model" "github.com/Team254/cheesy-arena/partner" "log" + "math/rand" "time" ) @@ -73,14 +74,15 @@ type Arena struct { AllianceSelectionNotifier *Notifier LowerThirdNotifier *Notifier ReloadDisplaysNotifier *Notifier - ScaleLeds led.Controller - RedSwitchLeds led.Controller - BlueSwitchLeds led.Controller Scale *game.Seesaw RedSwitch *game.Seesaw BlueSwitch *game.Seesaw RedVault *game.Vault BlueVault *game.Vault + ScaleLeds led.Controller + RedSwitchLeds led.Controller + BlueSwitchLeds led.Controller + warmupLedMode led.Mode } type ArenaStatus struct { @@ -234,9 +236,9 @@ func (arena *Arena) LoadMatch(match *model.Match) error { game.ResetPowerUps() // Set a consistent initial value for field element sidedness. - arena.Scale.SetSidedness(true) - arena.RedSwitch.SetSidedness(true) - arena.BlueSwitch.SetSidedness(true) + arena.Scale.NearIsRed = true + arena.RedSwitch.NearIsRed = true + arena.BlueSwitch.NearIsRed = true arena.ScaleLeds.SetSidedness(true) arena.RedSwitchLeds.SetSidedness(true) arena.BlueSwitchLeds.SetSidedness(true) @@ -321,9 +323,9 @@ func (arena *Arena) StartMatch() error { // Configure the field elements with the game-specific data. switchNearIsRed := arena.CurrentMatch.GameSpecificData[0] == 'L' scaleNearIsRed := arena.CurrentMatch.GameSpecificData[1] == 'L' - arena.Scale.SetSidedness(scaleNearIsRed) - arena.RedSwitch.SetSidedness(switchNearIsRed) - arena.BlueSwitch.SetSidedness(switchNearIsRed) + arena.Scale.NearIsRed = scaleNearIsRed + arena.RedSwitch.NearIsRed = switchNearIsRed + arena.BlueSwitch.NearIsRed = switchNearIsRed arena.ScaleLeds.SetSidedness(scaleNearIsRed) arena.RedSwitchLeds.SetSidedness(switchNearIsRed) arena.BlueSwitchLeds.SetSidedness(switchNearIsRed) @@ -411,12 +413,12 @@ func (arena *Arena) Update() { arena.AudienceDisplayScreen = "match" arena.AudienceDisplayNotifier.Notify(nil) arena.sendGameSpecificDataPacket() - arena.ScaleLeds.SetMode(led.WarmupMode, led.WarmupMode) - arena.RedSwitchLeds.SetMode(led.WarmupMode, led.WarmupMode) - arena.BlueSwitchLeds.SetMode(led.WarmupMode, led.WarmupMode) if !arena.MuteMatchSounds { arena.PlaySoundNotifier.Notify("match-warmup") } + // Pick an LED warmup mode at random to keep things interesting. + allWarmupModes := []led.Mode{led.WarmupMode, led.Warmup2Mode, led.Warmup3Mode, led.Warmup4Mode} + arena.warmupLedMode = allWarmupModes[rand.Intn(len(allWarmupModes))] case WarmupPeriod: auto = true enabled = false @@ -509,10 +511,7 @@ func (arena *Arena) Update() { // Handle field sensors/lights/motors. arena.handlePlcInput() arena.handlePlcOutput() - - arena.ScaleLeds.Update() - arena.RedSwitchLeds.Update() - arena.BlueSwitchLeds.Update() + arena.handleLeds() } // Loops indefinitely to track and update the arena components. @@ -751,6 +750,73 @@ func (arena *Arena) handlePlcOutput() { // TODO(patrick): Update for 2018. } +func (arena *Arena) handleLeds() { + switch arena.MatchState { + case WarmupPeriod: + arena.ScaleLeds.SetMode(arena.warmupLedMode, arena.warmupLedMode) + arena.RedSwitchLeds.SetMode(arena.warmupLedMode, arena.warmupLedMode) + arena.BlueSwitchLeds.SetMode(arena.warmupLedMode, arena.warmupLedMode) + case AutoPeriod: + fallthrough + case TeleopPeriod: + fallthrough + case EndgamePeriod: + handleSeesawTeleopLeds(arena.Scale, &arena.ScaleLeds) + handleSeesawTeleopLeds(arena.RedSwitch, &arena.RedSwitchLeds) + handleSeesawTeleopLeds(arena.BlueSwitch, &arena.BlueSwitchLeds) + case PausePeriod: + arena.ScaleLeds.SetMode(led.OffMode, led.OffMode) + arena.RedSwitchLeds.SetMode(led.OffMode, led.OffMode) + arena.BlueSwitchLeds.SetMode(led.OffMode, led.OffMode) + case PostMatch: + arena.ScaleLeds.SetMode(led.FadeSingleMode, led.FadeSingleMode) + arena.RedSwitchLeds.SetMode(led.FadeSingleMode, led.FadeSingleMode) + arena.BlueSwitchLeds.SetMode(led.FadeSingleMode, led.FadeSingleMode) + } + + arena.ScaleLeds.Update() + arena.RedSwitchLeds.Update() + arena.BlueSwitchLeds.Update() +} + +func handleSeesawTeleopLeds(seesaw *game.Seesaw, leds *led.Controller) { + // Assume the simplest mode to start and consider others in order of increasing complexity. + redMode := led.NotOwnedMode + blueMode := led.NotOwnedMode + + // Upgrade the mode to ownership based on the physical state of the switch or scale. + if seesaw.GetOwnedBy() == game.RedAlliance && seesaw.Kind != game.BlueAlliance { + redMode = led.OwnedMode + } else if seesaw.GetOwnedBy() == game.BlueAlliance && seesaw.Kind != game.RedAlliance { + blueMode = led.OwnedMode + } + + // Upgrade the mode if there is an applicable power up. + powerUp := game.GetActivePowerUp(time.Now()) + if powerUp != nil && (seesaw.Kind == game.NeitherAlliance && powerUp.Level >= 2 || + seesaw.Kind == powerUp.Alliance && (powerUp.Level == 1 || powerUp.Level == 3)) { + if powerUp.Effect == game.Boost { + if powerUp.Alliance == game.RedAlliance { + redMode = led.BoostMode + } else { + blueMode = led.BoostMode + } + } else { + if powerUp.Alliance == game.RedAlliance { + redMode = led.ForceMode + } else { + blueMode = led.ForceMode + } + } + } + + if seesaw.NearIsRed { + leds.SetMode(redMode, blueMode) + } else { + leds.SetMode(blueMode, redMode) + } +} + func (arena *Arena) handleEstop(station string, state bool) { allianceStation := arena.AllianceStations[station] if state { diff --git a/game/power_up.go b/game/power_up.go index ac7524a..ae0ce7c 100644 --- a/game/power_up.go +++ b/game/power_up.go @@ -12,11 +12,11 @@ import ( const powerUpDurationSec = 10 // Power up type/effect enum. -type effect int +type PowerUpEffect int const ( - force effect = iota - boost + Force PowerUpEffect = iota + Boost ) // Power up state enum. @@ -31,8 +31,8 @@ const ( type PowerUp struct { Alliance - effect - level int + Effect PowerUpEffect + Level int startTime time.Time } @@ -57,7 +57,7 @@ func (powerUp *PowerUp) getEndTime() time.Time { } // Returns the current active power up, or nil if there isn't one. -func getActivePowerUp(currentTime time.Time) *PowerUp { +func GetActivePowerUp(currentTime time.Time) *PowerUp { for _, powerUp := range powerUpUses { if powerUp.GetState(currentTime) == Active { return powerUp diff --git a/game/power_up_test.go b/game/power_up_test.go index 53cfca8..38717b2 100644 --- a/game/power_up_test.go +++ b/game/power_up_test.go @@ -31,13 +31,13 @@ func TestPowerUpActivate(t *testing.T) { assert.Equal(t, timeAfterStart(45), powerUp2.startTime) } - assert.Nil(t, getActivePowerUp(timeAfterStart(29.9))) - assert.Equal(t, powerUp1, getActivePowerUp(timeAfterStart(30.1))) - assert.Equal(t, powerUp1, getActivePowerUp(timeAfterStart(39.9))) - assert.Nil(t, getActivePowerUp(timeAfterStart(42))) - assert.Equal(t, powerUp2, getActivePowerUp(timeAfterStart(45.1))) - assert.Equal(t, powerUp2, getActivePowerUp(timeAfterStart(54.9))) - assert.Nil(t, getActivePowerUp(timeAfterStart(55.1))) + assert.Nil(t, GetActivePowerUp(timeAfterStart(29.9))) + assert.Equal(t, powerUp1, GetActivePowerUp(timeAfterStart(30.1))) + assert.Equal(t, powerUp1, GetActivePowerUp(timeAfterStart(39.9))) + assert.Nil(t, GetActivePowerUp(timeAfterStart(42))) + assert.Equal(t, powerUp2, GetActivePowerUp(timeAfterStart(45.1))) + assert.Equal(t, powerUp2, GetActivePowerUp(timeAfterStart(54.9))) + assert.Nil(t, GetActivePowerUp(timeAfterStart(55.1))) } func TestPowerUpQueue(t *testing.T) { @@ -56,8 +56,8 @@ func TestPowerUpQueue(t *testing.T) { powerUp3 := &PowerUp{Alliance: RedAlliance} assert.NotNil(t, maybeActivatePowerUp(powerUp3, timeAfterStart(81))) - assert.Equal(t, powerUp1, getActivePowerUp(timeAfterStart(69.9))) - assert.Equal(t, powerUp2, getActivePowerUp(timeAfterStart(70.1))) + assert.Equal(t, powerUp1, GetActivePowerUp(timeAfterStart(69.9))) + assert.Equal(t, powerUp2, GetActivePowerUp(timeAfterStart(70.1))) } func timeAfterStart(sec float32) time.Time { diff --git a/game/seesaw.go b/game/seesaw.go index 1256c88..b4865ec 100644 --- a/game/seesaw.go +++ b/game/seesaw.go @@ -19,7 +19,7 @@ const ( type Seesaw struct { Kind Alliance // Red or blue indicates that it is a switch; neither indicates the scale. - nearIsRed bool + NearIsRed bool ownerships []*Ownership } @@ -30,27 +30,21 @@ type Ownership struct { endTime *time.Time } -// Sets which side of the scale or switch belongs to which alliance. A value of true indicates that the side nearest the -// scoring table is red. -func (seesaw *Seesaw) SetSidedness(nearIsRed bool) { - seesaw.nearIsRed = nearIsRed -} - // Updates the internal timing state of the scale or switch given the current state of the sensors. func (seesaw *Seesaw) UpdateState(state [2]bool, currentTime time.Time) { ownedBy := NeitherAlliance // Check if there is an active force power up for this seesaw. - currentPowerUp := getActivePowerUp(currentTime) - if currentPowerUp != nil && currentPowerUp.effect == force && - (seesaw.Kind == NeitherAlliance && currentPowerUp.level >= 2 || - (seesaw.Kind == currentPowerUp.Alliance && (currentPowerUp.level == 1 || currentPowerUp.level == 3))) { + currentPowerUp := GetActivePowerUp(currentTime) + if currentPowerUp != nil && currentPowerUp.Effect == Force && + (seesaw.Kind == NeitherAlliance && currentPowerUp.Level >= 2 || + (seesaw.Kind == currentPowerUp.Alliance && (currentPowerUp.Level == 1 || currentPowerUp.Level == 3))) { ownedBy = currentPowerUp.Alliance } else { // Determine current ownership from sensor state. - if state[0] && !state[1] && seesaw.nearIsRed || state[1] && !state[0] && !seesaw.nearIsRed { + if state[0] && !state[1] && seesaw.NearIsRed || state[1] && !state[0] && !seesaw.NearIsRed { ownedBy = RedAlliance - } else if state[0] && !state[1] && !seesaw.nearIsRed || state[1] && !state[0] && seesaw.nearIsRed { + } else if state[0] && !state[1] && !seesaw.NearIsRed || state[1] && !state[0] && seesaw.NearIsRed { ownedBy = BlueAlliance } } @@ -131,9 +125,9 @@ func (ownership *Ownership) getSeconds(startTime, endTime time.Time, ignoreBoost // Find the boost power up applicable to this seesaw and alliance, if it exists. var boostPowerUp *PowerUp for _, powerUp := range powerUpUses { - if powerUp.effect == boost && ownership.ownedBy == powerUp.Alliance { - if ownership.seesaw.Kind == NeitherAlliance && powerUp.level >= 2 || - ownership.seesaw.Kind != NeitherAlliance && (powerUp.level == 1 || powerUp.level == 3) { + if powerUp.Effect == Boost && ownership.ownedBy == powerUp.Alliance { + if ownership.seesaw.Kind == NeitherAlliance && powerUp.Level >= 2 || + ownership.seesaw.Kind != NeitherAlliance && (powerUp.Level == 1 || powerUp.Level == 3) { boostPowerUp = powerUp break } diff --git a/game/seesaw_test.go b/game/seesaw_test.go index e3c5aa5..df19840 100644 --- a/game/seesaw_test.go +++ b/game/seesaw_test.go @@ -32,7 +32,7 @@ func TestSecondCounting(t *testing.T) { ResetPowerUps() redSwitch := &Seesaw{Kind: RedAlliance} - redSwitch.SetSidedness(true) + redSwitch.NearIsRed = true // Test that there is no accumulation before the start of the match. redSwitch.UpdateState([2]bool{true, false}, timeAfterStart(-20)) @@ -70,14 +70,14 @@ func TestForce(t *testing.T) { ResetPowerUps() blueSwitch := &Seesaw{Kind: BlueAlliance} - blueSwitch.SetSidedness(true) + blueSwitch.NearIsRed = true scale := &Seesaw{Kind: NeitherAlliance} - scale.SetSidedness(true) + scale.NearIsRed = true // Force switch only. blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(0)) scale.UpdateState([2]bool{true, false}, timeAfterStart(0)) - powerUp := &PowerUp{Alliance: BlueAlliance, effect: force, level: 1} + powerUp := &PowerUp{Alliance: BlueAlliance, Effect: Force, Level: 1} maybeActivatePowerUp(powerUp, timeAfterStart(2.5)) blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(2.5)) scale.UpdateState([2]bool{true, false}, timeAfterStart(2.5)) @@ -91,7 +91,7 @@ func TestForce(t *testing.T) { assert.Equal(t, 0.0, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(15))) // Force scale only. - powerUp = &PowerUp{Alliance: BlueAlliance, effect: force, level: 2} + powerUp = &PowerUp{Alliance: BlueAlliance, Effect: Force, Level: 2} maybeActivatePowerUp(powerUp, timeAfterStart(20)) blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(20)) scale.UpdateState([2]bool{true, false}, timeAfterStart(20)) @@ -101,7 +101,7 @@ func TestForce(t *testing.T) { assert.Equal(t, 10.0, scale.GetBlueSeconds(timeAfterStart(20), timeAfterStart(40))) // Force both switch and scale. - powerUp = &PowerUp{Alliance: BlueAlliance, effect: force, level: 3} + powerUp = &PowerUp{Alliance: BlueAlliance, Effect: Force, Level: 3} maybeActivatePowerUp(powerUp, timeAfterStart(50)) blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(50)) scale.UpdateState([2]bool{true, false}, timeAfterStart(50)) @@ -115,14 +115,14 @@ func TestBoost(t *testing.T) { ResetPowerUps() blueSwitch := &Seesaw{Kind: BlueAlliance} - blueSwitch.SetSidedness(true) + blueSwitch.NearIsRed = true scale := &Seesaw{Kind: NeitherAlliance} - scale.SetSidedness(false) + scale.NearIsRed = false // Test within continuous ownership period. blueSwitch.UpdateState([2]bool{false, true}, timeAfterStart(20)) scale.UpdateState([2]bool{true, false}, timeAfterStart(20)) - powerUp := &PowerUp{Alliance: BlueAlliance, effect: boost, level: 2} + powerUp := &PowerUp{Alliance: BlueAlliance, Effect: Boost, Level: 2} maybeActivatePowerUp(powerUp, timeAfterStart(25)) assert.Equal(t, 5.0, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(25))) assert.Equal(t, 6.0, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(25.5))) @@ -136,7 +136,7 @@ func TestBoost(t *testing.T) { ResetPowerUps() blueSwitch.UpdateState([2]bool{false, false}, timeAfterStart(44)) scale.UpdateState([2]bool{false, false}, timeAfterStart(44)) - powerUp = &PowerUp{Alliance: BlueAlliance, effect: boost, level: 3} + powerUp = &PowerUp{Alliance: BlueAlliance, Effect: Boost, Level: 3} maybeActivatePowerUp(powerUp, timeAfterStart(45)) assert.Equal(t, 0.0, blueSwitch.GetBlueSeconds(timeAfterStart(45), timeAfterStart(50))) assert.Equal(t, 0.0, scale.GetBlueSeconds(timeAfterStart(45), timeAfterStart(50))) @@ -151,7 +151,7 @@ func TestBoost(t *testing.T) { ResetPowerUps() scale.UpdateState([2]bool{false, true}, timeAfterStart(65)) assert.Equal(t, 5.0, scale.GetRedSeconds(timeAfterStart(65), timeAfterStart(70))) - powerUp = &PowerUp{Alliance: RedAlliance, effect: boost, level: 2} + powerUp = &PowerUp{Alliance: RedAlliance, Effect: Boost, Level: 2} maybeActivatePowerUp(powerUp, timeAfterStart(70)) scale.UpdateState([2]bool{false, false}, timeAfterStart(72.5)) assert.Equal(t, 10.0, scale.GetRedSeconds(timeAfterStart(65), timeAfterStart(72.5))) @@ -163,7 +163,7 @@ func TestBoost(t *testing.T) { // Test with just the switch. blueSwitch.UpdateState([2]bool{false, true}, timeAfterStart(100)) scale.UpdateState([2]bool{true, false}, timeAfterStart(100)) - powerUp = &PowerUp{Alliance: BlueAlliance, effect: boost, level: 1} + powerUp = &PowerUp{Alliance: BlueAlliance, Effect: Boost, Level: 1} maybeActivatePowerUp(powerUp, timeAfterStart(100)) assert.Equal(t, 20.0, blueSwitch.GetBlueSeconds(timeAfterStart(100), timeAfterStart(110))) assert.Equal(t, 10.0, scale.GetBlueSeconds(timeAfterStart(100), timeAfterStart(110))) diff --git a/game/vault.go b/game/vault.go index 10d779a..efe9fa3 100644 --- a/game/vault.go +++ b/game/vault.go @@ -35,16 +35,16 @@ func (vault *Vault) UpdateButtons(forceButton, levitateButton, boostButton bool, } if forceButton && vault.ForceCubes > 0 && vault.ForcePowerUp == nil { - vault.ForcePowerUp = maybeActivatePowerUp(&PowerUp{effect: force, Alliance: vault.Alliance, - level: vault.ForceCubes}, currentTime) + vault.ForcePowerUp = maybeActivatePowerUp(&PowerUp{Effect: Force, Alliance: vault.Alliance, + Level: vault.ForceCubes}, currentTime) if vault.ForcePowerUp != nil { vault.newlyPlayedPowerUp = "force" } } if boostButton && vault.BoostCubes > 0 && vault.BoostPowerUp == nil { - vault.BoostPowerUp = maybeActivatePowerUp(&PowerUp{effect: boost, Alliance: vault.Alliance, - level: vault.BoostCubes}, currentTime) + vault.BoostPowerUp = maybeActivatePowerUp(&PowerUp{Effect: Boost, Alliance: vault.Alliance, + Level: vault.BoostCubes}, currentTime) if vault.BoostPowerUp != nil { vault.newlyPlayedPowerUp = "boost" } diff --git a/game/vault_test.go b/game/vault_test.go index 010d267..e738d50 100644 --- a/game/vault_test.go +++ b/game/vault_test.go @@ -87,8 +87,8 @@ func TestVaultForce(t *testing.T) { vault.UpdateButtons(true, false, false, time.Now()) if assert.NotNil(t, vault.ForcePowerUp) { assert.Equal(t, BlueAlliance, vault.ForcePowerUp.Alliance) - assert.Equal(t, force, vault.ForcePowerUp.effect) - assert.Equal(t, 1, vault.ForcePowerUp.level) + assert.Equal(t, Force, vault.ForcePowerUp.Effect) + assert.Equal(t, 1, vault.ForcePowerUp.Level) } // Activation with two cubes. @@ -98,8 +98,8 @@ func TestVaultForce(t *testing.T) { vault.UpdateButtons(true, false, false, time.Now()) if assert.NotNil(t, vault.ForcePowerUp) { assert.Equal(t, RedAlliance, vault.ForcePowerUp.Alliance) - assert.Equal(t, force, vault.ForcePowerUp.effect) - assert.Equal(t, 2, vault.ForcePowerUp.level) + assert.Equal(t, Force, vault.ForcePowerUp.Effect) + assert.Equal(t, 2, vault.ForcePowerUp.Level) } // Activation with three cubes. @@ -110,8 +110,8 @@ func TestVaultForce(t *testing.T) { assert.NotNil(t, vault.ForcePowerUp) if assert.NotNil(t, vault.ForcePowerUp) { assert.Equal(t, BlueAlliance, vault.ForcePowerUp.Alliance) - assert.Equal(t, force, vault.ForcePowerUp.effect) - assert.Equal(t, 3, vault.ForcePowerUp.level) + assert.Equal(t, Force, vault.ForcePowerUp.Effect) + assert.Equal(t, 3, vault.ForcePowerUp.Level) } vault.UpdateCubes(3000, 0, 0) @@ -136,8 +136,8 @@ func TestVaultBoost(t *testing.T) { vault.UpdateButtons(false, false, true, time.Now()) if assert.NotNil(t, vault.BoostPowerUp) { assert.Equal(t, BlueAlliance, vault.BoostPowerUp.Alliance) - assert.Equal(t, boost, vault.BoostPowerUp.effect) - assert.Equal(t, 1, vault.BoostPowerUp.level) + assert.Equal(t, Boost, vault.BoostPowerUp.Effect) + assert.Equal(t, 1, vault.BoostPowerUp.Level) } // Activation with two cubes. @@ -147,8 +147,8 @@ func TestVaultBoost(t *testing.T) { vault.UpdateButtons(false, false, true, time.Now()) if assert.NotNil(t, vault.BoostPowerUp) { assert.Equal(t, RedAlliance, vault.BoostPowerUp.Alliance) - assert.Equal(t, boost, vault.BoostPowerUp.effect) - assert.Equal(t, 2, vault.BoostPowerUp.level) + assert.Equal(t, Boost, vault.BoostPowerUp.Effect) + assert.Equal(t, 2, vault.BoostPowerUp.Level) } // Activation with three cubes. @@ -159,8 +159,8 @@ func TestVaultBoost(t *testing.T) { assert.NotNil(t, vault.BoostPowerUp) if assert.NotNil(t, vault.BoostPowerUp) { assert.Equal(t, BlueAlliance, vault.BoostPowerUp.Alliance) - assert.Equal(t, boost, vault.BoostPowerUp.effect) - assert.Equal(t, 3, vault.BoostPowerUp.level) + assert.Equal(t, Boost, vault.BoostPowerUp.Effect) + assert.Equal(t, 3, vault.BoostPowerUp.Level) } vault.UpdateCubes(0, 0, 3000) diff --git a/led/color.go b/led/color.go index db3e72e..ac1b288 100644 --- a/led/color.go +++ b/led/color.go @@ -35,6 +35,6 @@ var colors = map[color][3]byte{ black: {0, 0, 0}, purpleRed: {200, 0, 50}, purpleBlue: {50, 0, 200}, - dimRed: {100, 0, 0}, - dimBlue: {0, 0, 100}, + dimRed: {50, 0, 0}, + dimBlue: {0, 0, 50}, } diff --git a/led/controller.go b/led/controller.go index 14f9d59..b4cfe92 100644 --- a/led/controller.go +++ b/led/controller.go @@ -45,10 +45,14 @@ func (controller *Controller) SetAddress(address string) error { // Sets the current LED sequence mode and resets the intra-sequence counter to the beginning. func (controller *Controller) SetMode(nearMode, farMode Mode) { - controller.nearStrip.currentMode = nearMode - controller.nearStrip.counter = 0 - controller.farStrip.currentMode = farMode - controller.farStrip.counter = 0 + 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. diff --git a/led/mode.go b/led/mode.go index 39cbc25..b07560d 100644 --- a/led/mode.go +++ b/led/mode.go @@ -23,28 +23,30 @@ const ( ForceMode BoostMode RandomMode - FadeMode + FadeRedBlueMode + FadeSingleMode GradientMode BlinkMode ) var ModeNames = map[Mode]string{ - OffMode: "Off", - RedMode: "Red", - GreenMode: "Green", - BlueMode: "Blue", - WhiteMode: "White", - ChaseMode: "Chase", - WarmupMode: "Warmup", - Warmup2Mode: "Warmup Purple", - Warmup3Mode: "Warmup Sneaky", - Warmup4Mode: "Warmup Gradient", - OwnedMode: "Owned", - NotOwnedMode: "Not Owned", - ForceMode: "Force", - BoostMode: "Boost", - RandomMode: "Random", - FadeMode: "Fade", - GradientMode: "Gradient", - BlinkMode: "Blink", + OffMode: "Off", + RedMode: "Red", + GreenMode: "Green", + BlueMode: "Blue", + WhiteMode: "White", + ChaseMode: "Chase", + WarmupMode: "Warmup", + Warmup2Mode: "Warmup Purple", + Warmup3Mode: "Warmup Sneaky", + Warmup4Mode: "Warmup Gradient", + OwnedMode: "Owned", + NotOwnedMode: "Not Owned", + ForceMode: "Force", + BoostMode: "Boost", + RandomMode: "Random", + FadeRedBlueMode: "Fade Red/Blue", + FadeSingleMode: "Fade Single", + GradientMode: "Gradient", + BlinkMode: "Blink", } diff --git a/led/strip.go b/led/strip.go index b97431e..aca5696 100644 --- a/led/strip.go +++ b/led/strip.go @@ -50,8 +50,10 @@ func (strip *strip) updatePixels() { strip.updateBoostMode() case RandomMode: strip.updateRandomMode() - case FadeMode: - strip.updateFadeMode() + case FadeRedBlueMode: + strip.updateFadeRedBlueMode() + case FadeSingleMode: + strip.updateFadeSingleMode() case GradientMode: strip.updateGradientMode() case BlinkMode: @@ -117,6 +119,14 @@ func (strip *strip) getDimColor() color { return dimBlue } +// Returns a dim version of the opposite primary color (red or blue) of this strip. +func (strip *strip) getDimOppositeColor() color { + if strip.isRed { + return dimBlue + } + return dimRed +} + // Returns the starting offset for the gradient mode for this strip. func (strip *strip) getGradientStartOffset() int { if strip.isRed { @@ -231,10 +241,13 @@ func (strip *strip) updateOwnedMode() { return } for i := 0; i < numPixels; i++ { - if i%pixelSpacing == strip.counter/speedDivisor%pixelSpacing { - strip.pixels[i] = colors[strip.getColor()] - } else { - strip.pixels[i] = colors[black] + switch (i + strip.counter/speedDivisor) % pixelSpacing { + case 0: + fallthrough + case 1: + strip.pixels[len(strip.pixels)-i-1] = colors[strip.getColor()] + default: + strip.pixels[len(strip.pixels)-i-1] = colors[black] } } } @@ -256,9 +269,9 @@ func (strip *strip) updateForceMode() { case 2: fallthrough case 4: - strip.pixels[i] = colors[strip.getOppositeColor()] + strip.pixels[i] = colors[strip.getColor()] case 3: - strip.pixels[i] = colors[strip.getDimColor()] + strip.pixels[i] = colors[strip.getDimOppositeColor()] default: strip.pixels[i] = colors[black] } @@ -289,7 +302,7 @@ func (strip *strip) updateRandomMode() { } } -func (strip *strip) updateFadeMode() { +func (strip *strip) updateFadeRedBlueMode() { fadeCycles := 40 holdCycles := 10 if strip.counter == 4*holdCycles+4*fadeCycles { @@ -317,6 +330,24 @@ func (strip *strip) updateFadeMode() { } } +func (strip *strip) updateFadeSingleMode() { + offCycles := 50 + fadeCycles := 100 + if strip.counter == offCycles+2*fadeCycles { + strip.counter = 0 + } + + for i := 0; i < numPixels; 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 *strip) updateGradientMode() { for i := 0; i < numPixels; i++ { strip.pixels[numPixels-i-1] = getGradientColor(i+strip.counter, 75)