From dc48dc7f7babce55c4e2fc6a366eec901009a1a5 Mon Sep 17 00:00:00 2001 From: Colby Skeggs Date: Thu, 17 Jul 2014 11:01:14 -0700 Subject: [PATCH] Implemented FRCSim artf2628, fixed bugs in non-sim Relay.java and sim PWM.cpp. Change-Id: Ic00fcd5026ce0570c79a65be178e45eeb94b3deb --- wpilibc/wpilibC++Sim/include/Relay.h | 65 ++++ wpilibc/wpilibC++Sim/src/PWM.cpp | 2 +- wpilibc/wpilibC++Sim/src/Relay.cpp | 198 ++++++++++ .../java/edu/wpi/first/wpilibj/Relay.java | 2 +- .../java/edu/wpi/first/wpilibj/Relay.java | 345 ++++++++++++++++++ .../edu/wpi/first/wpilibj/SensorBase.java | 6 +- 6 files changed, 613 insertions(+), 5 deletions(-) create mode 100644 wpilibc/wpilibC++Sim/include/Relay.h create mode 100644 wpilibc/wpilibC++Sim/src/Relay.cpp create mode 100644 wpilibj/wpilibJavaSim/src/main/java/edu/wpi/first/wpilibj/Relay.java diff --git a/wpilibc/wpilibC++Sim/include/Relay.h b/wpilibc/wpilibC++Sim/include/Relay.h new file mode 100644 index 0000000000..35433d5357 --- /dev/null +++ b/wpilibc/wpilibC++Sim/include/Relay.h @@ -0,0 +1,65 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2008. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */ +/*----------------------------------------------------------------------------*/ +#pragma once + +#include "SensorBase.h" +#include "simulation/SimContinuousOutput.h" +#include "tables/ITableListener.h" +#include "LiveWindow/LiveWindowSendable.h" +#include "tables/ITable.h" + +class DigitalModule; + +/** + * Class for Spike style relay outputs. + * Relays are intended to be connected to spikes or similar relays. The relay channels controls + * a pair of pins that are either both off, one on, the other on, or both on. This translates into + * two spike outputs at 0v, one at 12v and one at 0v, one at 0v and the other at 12v, or two + * spike outputs at 12V. This allows off, full forward, or full reverse control of motors without + * variable speed. It also allows the two channels (forward and reverse) to be used independently + * for something that does not care about voltage polatiry (like a solenoid). + */ +class Relay : public SensorBase, public ITableListener, public LiveWindowSendable +{ +public: + enum Value + { + kOff, + kOn, + kForward, + kReverse + }; + enum Direction + { + kBothDirections, + kForwardOnly, + kReverseOnly + }; + + Relay(uint32_t channel, Direction direction = kBothDirections); + virtual ~Relay(); + + void Set(Value value); + Value Get(); + + void ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew); + void UpdateTable(); + void StartLiveWindowMode(); + void StopLiveWindowMode(); + std::string GetSmartDashboardType(); + void InitTable(ITable *subTable); + ITable * GetTable(); + + ITable *m_table; + +private: + void InitRelay(); + + uint32_t m_channel; + Direction m_direction; + SimContinuousOutput* impl; + bool go_pos, go_neg; +}; diff --git a/wpilibc/wpilibC++Sim/src/PWM.cpp b/wpilibc/wpilibC++Sim/src/PWM.cpp index f077d42ab2..c581501965 100644 --- a/wpilibc/wpilibC++Sim/src/PWM.cpp +++ b/wpilibc/wpilibC++Sim/src/PWM.cpp @@ -34,7 +34,7 @@ void PWM::InitPWM(uint32_t channel) return; } - sprintf(buf, "pwm/1/%d", channel); + sprintf(buf, "pwm/%d", channel); impl = new SimContinuousOutput(buf); m_channel = channel; m_eliminateDeadband = false; diff --git a/wpilibc/wpilibC++Sim/src/Relay.cpp b/wpilibc/wpilibC++Sim/src/Relay.cpp new file mode 100644 index 0000000000..a65d7a2b5c --- /dev/null +++ b/wpilibc/wpilibC++Sim/src/Relay.cpp @@ -0,0 +1,198 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2008. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */ +/*----------------------------------------------------------------------------*/ + +#include "Relay.h" + +//#include "NetworkCommunication/UsageReporting.h" +#include "WPIErrors.h" +#include "LiveWindow/LiveWindow.h" + +/** + * Common relay initialization method. + * This code is common to all Relay constructors and initializes the relay and reserves + * all resources that need to be locked. Initially the relay is set to both lines at 0v. + */ +void Relay::InitRelay() +{ + m_table = NULL; + char buf[64]; + if (!SensorBase::CheckRelayChannel(m_channel)) + { + snprintf(buf, 64, "Relay Channel %d", m_channel); + wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf); + return; + } + + sprintf(buf, "relay/%d", m_channel); + impl = new SimContinuousOutput(buf); // TODO: Allow two different relays (targetting the different halves of a relay) to be combined to control one motor. + LiveWindow::GetInstance()->AddActuator("Relay", 1, m_channel, this); + go_pos = go_neg = false; +} + +/** + * Relay constructor given a channel. + * @param channel The channel number. + * @param direction The direction that the Relay object will control. + */ +Relay::Relay(uint32_t channel, Relay::Direction direction) + : m_channel (channel) + , m_direction (direction) +{ + InitRelay(); +} + +/** + * Free the resource associated with a relay. + * The relay channels are set to free and the relay output is turned off. + */ +Relay::~Relay() +{ + impl->Set(0); +} + +/** + * Set the relay state. + * + * Valid values depend on which directions of the relay are controlled by the object. + * + * When set to kBothDirections, the relay can be any of the four states: + * 0v-0v, 0v-12v, 12v-0v, 12v-12v + * + * When set to kForwardOnly or kReverseOnly, you can specify the constant for the + * direction or you can simply specify kOff and kOn. Using only kOff and kOn is + * recommended. + * + * @param value The state to set the relay. + */ +void Relay::Set(Relay::Value value) +{ + switch (value) + { + case kOff: + if (m_direction == kBothDirections || m_direction == kForwardOnly) + { + go_pos = false; + } + if (m_direction == kBothDirections || m_direction == kReverseOnly) + { + go_neg = false; + } + break; + case kOn: + if (m_direction == kBothDirections || m_direction == kForwardOnly) + { + go_pos = true; + } + if (m_direction == kBothDirections || m_direction == kReverseOnly) + { + go_neg = true; + } + break; + case kForward: + if (m_direction == kReverseOnly) + { + wpi_setWPIError(IncompatibleMode); + break; + } + if (m_direction == kBothDirections || m_direction == kForwardOnly) + { + go_pos = true; + } + if (m_direction == kBothDirections) + { + go_neg = false; + } + break; + case kReverse: + if (m_direction == kForwardOnly) + { + wpi_setWPIError(IncompatibleMode); + break; + } + if (m_direction == kBothDirections) + { + go_pos = false; + } + if (m_direction == kBothDirections || m_direction == kReverseOnly) + { + go_neg = true; + } + break; + } + impl->Set((go_pos ? 1 : 0) + (go_neg ? -1 : 0)); +} + +/** + * Get the Relay State + * + * Gets the current state of the relay. + * + * When set to kForwardOnly or kReverseOnly, value is returned as kOn/kOff not + * kForward/kReverse (per the recommendation in Set) + * + * @return The current state of the relay as a Relay::Value + */ +Relay::Value Relay::Get() { + // TODO: Don't assume that the go_pos and go_neg fields are correct? + if ((go_pos || m_direction == kReverseOnly) && (go_neg || m_direction == kForwardOnly)) { + return kOn; + } else if (go_pos) { + return kForward; + } else if (go_neg) { + return kReverse; + } else { + return kOff; + } +} + +void Relay::ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew) { + std::string *val = (std::string *) value.ptr; + if (*val == "Off") Set(kOff); + else if (*val == "Forward") Set(kForward); + else if (*val == "Reverse") Set(kReverse); +} + +void Relay::UpdateTable() { + if(m_table != NULL){ + if (Get() == kOn) { + m_table->PutString("Value", "On"); + } + else if (Get() == kForward) { + m_table->PutString("Value", "Forward"); + } + else if (Get() == kReverse) { + m_table->PutString("Value", "Reverse"); + } + else { + m_table->PutString("Value", "Off"); + } + } +} + +void Relay::StartLiveWindowMode() { + if(m_table != NULL){ + m_table->AddTableListener("Value", this, true); + } +} + +void Relay::StopLiveWindowMode() { + if(m_table != NULL){ + m_table->RemoveTableListener(this); + } +} + +std::string Relay::GetSmartDashboardType() { + return "Relay"; +} + +void Relay::InitTable(ITable *subTable) { + m_table = subTable; + UpdateTable(); +} + +ITable * Relay::GetTable() { + return m_table; +} diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/Relay.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/Relay.java index 902022f4a3..44efc7cf5c 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/Relay.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/Relay.java @@ -299,7 +299,7 @@ public class Relay extends SensorBase implements IDeviceController, } } else { if (RelayJNI.getRelayReverse(m_port, status.asIntBuffer()) != 0) { - if (m_direction == Direction.kForward) { + if (m_direction == Direction.kReverse) { return Value.kOn; } else { return Value.kReverse; diff --git a/wpilibj/wpilibJavaSim/src/main/java/edu/wpi/first/wpilibj/Relay.java b/wpilibj/wpilibJavaSim/src/main/java/edu/wpi/first/wpilibj/Relay.java new file mode 100644 index 0000000000..fcc327a4bc --- /dev/null +++ b/wpilibj/wpilibJavaSim/src/main/java/edu/wpi/first/wpilibj/Relay.java @@ -0,0 +1,345 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2008-2012. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package edu.wpi.first.wpilibj; + +import edu.wpi.first.wpilibj.livewindow.LiveWindow; +import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; +import edu.wpi.first.wpilibj.simulation.SimSpeedController; +import edu.wpi.first.wpilibj.parsing.IDeviceController; +import edu.wpi.first.wpilibj.tables.ITable; +import edu.wpi.first.wpilibj.tables.ITableListener; + +/** + * Class for VEX Robotics Spike style relay outputs. Relays are intended to be + * connected to Spikes or similar relays. The relay channels controls a pair of + * pins that are either both off, one on, the other on, or both on. This + * translates into two Spike outputs at 0v, one at 12v and one at 0v, one at 0v + * and the other at 12v, or two Spike outputs at 12V. This allows off, full + * forward, or full reverse control of motors without variable speed. It also + * allows the two channels (forward and reverse) to be used independently for + * something that does not care about voltage polarity (like a solenoid). + */ +public class Relay extends SensorBase implements IDeviceController, + LiveWindowSendable { + /** + * This class represents errors in trying to set relay values contradictory + * to the direction to which the relay is set. + */ + public class InvalidValueException extends RuntimeException { + + /** + * Create a new exception with the given message + * + * @param message + * the message to pass with the exception + */ + public InvalidValueException(String message) { + super(message); + } + } + + /** + * The state to drive a Relay to. + */ + public static class Value { + + /** + * The integer value representing this enumeration + */ + public final int value; + static final int kOff_val = 0; + static final int kOn_val = 1; + static final int kForward_val = 2; + static final int kReverse_val = 3; + /** + * value: off + */ + public static final Value kOff = new Value(kOff_val); + /** + * value: on for relays with defined direction + */ + public static final Value kOn = new Value(kOn_val); + /** + * value: forward + */ + public static final Value kForward = new Value(kForward_val); + /** + * value: reverse + */ + public static final Value kReverse = new Value(kReverse_val); + + private Value(int value) { + this.value = value; + } + } + + /** + * The Direction(s) that a relay is configured to operate in. + */ + public static class Direction { + + /** + * The integer value representing this enumeration + */ + public final int value; + static final int kBoth_val = 0; + static final int kForward_val = 1; + static final int kReverse_val = 2; + /** + * direction: both directions are valid + */ + public static final Direction kBoth = new Direction(kBoth_val); + /** + * direction: Only forward is valid + */ + public static final Direction kForward = new Direction(kForward_val); + /** + * direction: only reverse is valid + */ + public static final Direction kReverse = new Direction(kReverse_val); + + private Direction(int value) { + this.value = value; + } + } + + private int m_channel; + private Direction m_direction; + private SimSpeedController impl; + private boolean go_pos, go_neg; + + /** + * Common relay initialization method. This code is common to all Relay + * constructors and initializes the relay and reserves all resources that + * need to be locked. Initially the relay is set to both lines at 0v. + */ + private void initRelay() { + SensorBase.checkRelayChannel(m_channel); + impl = new SimSpeedController("simulator/relay/" + m_channel); + LiveWindow.addActuator("Relay", m_channel, this); + } + + /** + * Relay constructor given a channel. + * + * @param channel + * The channel number for this relay. + * @param direction + * The direction that the Relay object will control. + */ + public Relay(final int channel, Direction direction) { + if (direction == null) + throw new NullPointerException("Null Direction was given"); + m_channel = channel; + m_direction = direction; + initRelay(); + } + + /** + * Relay constructor given a channel, allowing both directions. + * + * @param channel + * The channel number for this relay. + */ + public Relay(final int channel) { + m_channel = channel; + m_direction = Direction.kBoth; + initRelay(); + } + + public void free() { + impl.set(0); + } + + /** + * Set the relay state. + * + * Valid values depend on which directions of the relay are controlled by + * the object. + * + * When set to kBothDirections, the relay can be set to any of the four + * states: 0v-0v, 12v-0v, 0v-12v, 12v-12v + * + * When set to kForwardOnly or kReverseOnly, you can specify the constant + * for the direction or you can simply specify kOff_val and kOn_val. Using + * only kOff_val and kOn_val is recommended. + * + * @param value + * The state to set the relay. + */ + public void set(Value value) { + switch (value.value) { + case Value.kOff_val: + if (m_direction == Direction.kBoth + || m_direction == Direction.kForward) { + go_pos = false; + } + if (m_direction == Direction.kBoth + || m_direction == Direction.kReverse) { + go_neg = false; + } + break; + case Value.kOn_val: + if (m_direction == Direction.kBoth + || m_direction == Direction.kForward) { + go_pos = true; + } + if (m_direction == Direction.kBoth + || m_direction == Direction.kReverse) { + go_neg = true; + } + break; + case Value.kForward_val: + if (m_direction == Direction.kReverse) + throw new InvalidValueException( + "A relay configured for reverse cannot be set to forward"); + if (m_direction == Direction.kBoth + || m_direction == Direction.kForward) { + + go_pos = true; + } + if (m_direction == Direction.kBoth) { + go_neg = false; + } + break; + case Value.kReverse_val: + if (m_direction == Direction.kForward) + throw new InvalidValueException( + "A relay configured for forward cannot be set to reverse"); + if (m_direction == Direction.kBoth) { + go_pos = false; + } + if (m_direction == Direction.kBoth + || m_direction == Direction.kReverse) { + go_neg = true; + } + break; + default: + // Cannot hit this, limited by Value enum + } + impl.set((go_pos ? 1 : 0) + (go_neg ? -1 : 0)); + } + + /** + * Get the Relay State + * + * Gets the current state of the relay. + * + * When set to kForwardOnly or kReverseOnly, value is returned as kOn/kOff + * not kForward/kReverse (per the recommendation in Set) + * + * @return The current state of the relay as a Relay::Value + */ + public Value get() { + // TODO: Don't assume that the go_pos and go_neg fields are correct? + if ((go_pos || m_direction == Direction.kReverse) && (go_neg || m_direction == Direction.kForward)) { + return Value.kOn; + } else if (go_pos) { + return Value.kForward; + } else if (go_neg) { + return Value.kReverse; + } else { + return Value.kOff; + } + } + + /** + * Set the Relay Direction + * + * Changes which values the relay can be set to depending on which direction + * is used + * + * Valid inputs are kBothDirections, kForwardOnly, and kReverseOnly + * + * @param direction + * The direction for the relay to operate in + */ + public void setDirection(Direction direction) { + if (direction == null) + throw new NullPointerException("Null Direction was given"); + if (m_direction == direction) { + return; + } + + free(); + + m_direction = direction; + + // initRelay(); // NOTE: not needed in simulation + } + + /* + * Live Window code, only does anything if live window is activated. + */ + public String getSmartDashboardType() { + return "Relay"; + } + + private ITable m_table; + private ITableListener m_table_listener; + + /** + * {@inheritDoc} + */ + public void initTable(ITable subtable) { + m_table = subtable; + updateTable(); + } + + /** + * {@inheritDoc} + */ + public ITable getTable() { + return m_table; + } + + /** + * {@inheritDoc} + */ + public void updateTable() { + if (m_table != null) { + if (get() == Value.kOn) { + m_table.putString("Value", "On"); + } else if (get() == Value.kForward) { + m_table.putString("Value", "Forward"); + } else if (get() == Value.kReverse) { + m_table.putString("Value", "Reverse"); + } else { + m_table.putString("Value", "Off"); + } + } + } + + /** + * {@inheritDoc} + */ + public void startLiveWindowMode() { + m_table_listener = new ITableListener() { + public void valueChanged(ITable itable, String key, Object value, + boolean bln) { + String val = ((String) value); + if (val.equals("Off")) { + set(Value.kOff); + } else if (val.equals("Forward")) { + set(Value.kForward); + } else if (val.equals("Reverse")) { + set(Value.kReverse); + } + } + }; + m_table.addTableListener("Value", m_table_listener, true); + } + + /** + * {@inheritDoc} + */ + public void stopLiveWindowMode() { + // TODO: Broken, should only remove the listener from "Value" only. + m_table.removeTableListener(m_table_listener); + } +} diff --git a/wpilibj/wpilibJavaSim/src/main/java/edu/wpi/first/wpilibj/SensorBase.java b/wpilibj/wpilibJavaSim/src/main/java/edu/wpi/first/wpilibj/SensorBase.java index 4c2287d277..8dd788458d 100644 --- a/wpilibj/wpilibJavaSim/src/main/java/edu/wpi/first/wpilibj/SensorBase.java +++ b/wpilibj/wpilibJavaSim/src/main/java/edu/wpi/first/wpilibj/SensorBase.java @@ -173,7 +173,7 @@ public abstract class SensorBase { // TODO: Refactor * @param channel The channel number to check. */ protected static void checkDigitalChannel(final int channel) { - if (channel < 0 || channel >= kDigitalChannels) { + if (channel <= 0 || channel > kDigitalChannels) { System.err.println("Requested digital channel number is out of range."); } } @@ -186,7 +186,7 @@ public abstract class SensorBase { // TODO: Refactor * @param channel The channel number to check. */ protected static void checkRelayChannel(final int channel) { - if (channel < 0 || channel >= kRelayChannels) { + if (channel <= 0 || channel > kRelayChannels) { System.err.println("Requested relay channel number is out of range."); throw new IndexOutOfBoundsException("Requested relay channel number is out of range."); } @@ -200,7 +200,7 @@ public abstract class SensorBase { // TODO: Refactor * @param channel The channel number to check. */ protected static void checkPWMChannel(final int channel) { - if (channel < 0 || channel >= kPwmChannels) { + if (channel <= 0 || channel > kPwmChannels) { System.err.println("Requested PWM channel number is out of range."); throw new IndexOutOfBoundsException("Requested PWM channel number is out of range."); }