mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-23 01:21:42 +00:00
Update to 2018_v4 image and new build system. (#598)
* Revert "Force OpenCV to 3.1.0 (#602)"
This reverts commit 50ed55e8e2.
* Removes Simulation
* Removes old build system
* Removes old gtest
* Adds new gmock and gtest
* Updates to new ni-libraries
* removes MyRobot (to be replaced)
* moves files to new location
* Adds new sim backend and new test executables
* updates .styleguide and .gitignore
* Changes cpp WPILibVersion to a function
MSVC throws an AV with the old version.
* Disables USBCamera on all systems except for linux
* 2018 NI Libraries
* New build system
This commit is contained in:
committed by
Peter Johnson
parent
50ed55e8e2
commit
e1195e8b9d
206
wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java
Normal file
206
wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java
Normal file
@@ -0,0 +1,206 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tInstances;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.interfaces.Accelerometer;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* ADXL345 I2C Accelerometer.
|
||||
*/
|
||||
@SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"})
|
||||
public class ADXL345_I2C extends SensorBase implements Accelerometer, LiveWindowSendable {
|
||||
|
||||
private static final byte kAddress = 0x1D;
|
||||
private static final byte kPowerCtlRegister = 0x2D;
|
||||
private static final byte kDataFormatRegister = 0x31;
|
||||
private static final byte kDataRegister = 0x32;
|
||||
private static final double kGsPerLSB = 0.00390625;
|
||||
private static final byte kPowerCtl_Link = 0x20;
|
||||
private static final byte kPowerCtl_AutoSleep = 0x10;
|
||||
private static final byte kPowerCtl_Measure = 0x08;
|
||||
private static final byte kPowerCtl_Sleep = 0x04;
|
||||
|
||||
private static final byte kDataFormat_SelfTest = (byte) 0x80;
|
||||
private static final byte kDataFormat_SPI = 0x40;
|
||||
private static final byte kDataFormat_IntInvert = 0x20;
|
||||
private static final byte kDataFormat_FullRes = 0x08;
|
||||
private static final byte kDataFormat_Justify = 0x04;
|
||||
|
||||
public enum Axes {
|
||||
kX((byte) 0x00),
|
||||
kY((byte) 0x02),
|
||||
kZ((byte) 0x04);
|
||||
|
||||
/**
|
||||
* The integer value representing this enumeration.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public final byte value;
|
||||
|
||||
private Axes(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public static class AllAxes {
|
||||
|
||||
public double XAxis;
|
||||
public double YAxis;
|
||||
public double ZAxis;
|
||||
}
|
||||
|
||||
protected I2C m_i2c;
|
||||
|
||||
/**
|
||||
* Constructs the ADXL345 Accelerometer with I2C address 0x1D.
|
||||
*
|
||||
* @param port The I2C port the accelerometer is attached to
|
||||
* @param range The range (+ or -) that the accelerometer will measure.
|
||||
*/
|
||||
public ADXL345_I2C(I2C.Port port, Range range) {
|
||||
this(port, range, kAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the ADXL345 Accelerometer over I2C.
|
||||
*
|
||||
* @param port The I2C port the accelerometer is attached to
|
||||
* @param range The range (+ or -) that the accelerometer will measure.
|
||||
* @param deviceAddress I2C address of the accelerometer (0x1D or 0x53)
|
||||
*/
|
||||
public ADXL345_I2C(I2C.Port port, Range range, int deviceAddress) {
|
||||
m_i2c = new I2C(port, deviceAddress);
|
||||
|
||||
// Turn on the measurements
|
||||
m_i2c.write(kPowerCtlRegister, kPowerCtl_Measure);
|
||||
|
||||
setRange(range);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_ADXL345, tInstances.kADXL345_I2C);
|
||||
LiveWindow.addSensor("ADXL345_I2C", port.value, this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setRange(Range range) {
|
||||
final byte value;
|
||||
|
||||
switch (range) {
|
||||
case k2G:
|
||||
value = 0;
|
||||
break;
|
||||
case k4G:
|
||||
value = 1;
|
||||
break;
|
||||
case k8G:
|
||||
value = 2;
|
||||
break;
|
||||
case k16G:
|
||||
value = 3;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(range + " unsupported range type");
|
||||
}
|
||||
|
||||
// Specify the data format to read
|
||||
m_i2c.write(kDataFormatRegister, kDataFormat_FullRes | value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return getAcceleration(Axes.kX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return getAcceleration(Axes.kY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ() {
|
||||
return getAcceleration(Axes.kZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the acceleration of one axis in Gs.
|
||||
*
|
||||
* @param axis The axis to read from.
|
||||
* @return Acceleration of the ADXL345 in Gs.
|
||||
*/
|
||||
public double getAcceleration(Axes axis) {
|
||||
ByteBuffer rawAccel = ByteBuffer.allocateDirect(2);
|
||||
m_i2c.read(kDataRegister + axis.value, 2, rawAccel);
|
||||
|
||||
// Sensor is little endian... swap bytes
|
||||
rawAccel.order(ByteOrder.LITTLE_ENDIAN);
|
||||
return rawAccel.getShort(0) * kGsPerLSB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the acceleration of all axes in Gs.
|
||||
*
|
||||
* @return An object containing the acceleration measured on each axis of the ADXL345 in Gs.
|
||||
*/
|
||||
public AllAxes getAccelerations() {
|
||||
AllAxes data = new AllAxes();
|
||||
ByteBuffer rawData = ByteBuffer.allocateDirect(6);
|
||||
m_i2c.read(kDataRegister, 6, rawData);
|
||||
|
||||
// Sensor is little endian... swap bytes
|
||||
rawData.order(ByteOrder.LITTLE_ENDIAN);
|
||||
data.XAxis = rawData.getShort(0) * kGsPerLSB;
|
||||
data.YAxis = rawData.getShort(2) * kGsPerLSB;
|
||||
data.ZAxis = rawData.getShort(4) * kGsPerLSB;
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "3AxisAccelerometer";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("X", getX());
|
||||
m_table.putNumber("Y", getY());
|
||||
m_table.putNumber("Z", getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
224
wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java
Normal file
224
wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java
Normal file
@@ -0,0 +1,224 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tInstances;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.interfaces.Accelerometer;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* ADXL345 SPI Accelerometer.
|
||||
*/
|
||||
@SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"})
|
||||
public class ADXL345_SPI extends SensorBase implements Accelerometer, LiveWindowSendable {
|
||||
private static final int kPowerCtlRegister = 0x2D;
|
||||
private static final int kDataFormatRegister = 0x31;
|
||||
private static final int kDataRegister = 0x32;
|
||||
private static final double kGsPerLSB = 0.00390625;
|
||||
|
||||
private static final int kAddress_Read = 0x80;
|
||||
private static final int kAddress_MultiByte = 0x40;
|
||||
|
||||
private static final int kPowerCtl_Link = 0x20;
|
||||
private static final int kPowerCtl_AutoSleep = 0x10;
|
||||
private static final int kPowerCtl_Measure = 0x08;
|
||||
private static final int kPowerCtl_Sleep = 0x04;
|
||||
|
||||
private static final int kDataFormat_SelfTest = 0x80;
|
||||
private static final int kDataFormat_SPI = 0x40;
|
||||
private static final int kDataFormat_IntInvert = 0x20;
|
||||
private static final int kDataFormat_FullRes = 0x08;
|
||||
private static final int kDataFormat_Justify = 0x04;
|
||||
|
||||
public enum Axes {
|
||||
kX((byte) 0x00),
|
||||
kY((byte) 0x02),
|
||||
kZ((byte) 0x04);
|
||||
|
||||
/**
|
||||
* The integer value representing this enumeration.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public final byte value;
|
||||
|
||||
private Axes(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public static class AllAxes {
|
||||
|
||||
public double XAxis;
|
||||
public double YAxis;
|
||||
public double ZAxis;
|
||||
}
|
||||
|
||||
protected SPI m_spi;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param port The SPI port that the accelerometer is connected to
|
||||
* @param range The range (+ or -) that the accelerometer will measure.
|
||||
*/
|
||||
public ADXL345_SPI(SPI.Port port, Range range) {
|
||||
m_spi = new SPI(port);
|
||||
init(range);
|
||||
LiveWindow.addSensor("ADXL345_SPI", port.value, this);
|
||||
}
|
||||
|
||||
public void free() {
|
||||
m_spi.free();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set SPI bus parameters, bring device out of sleep and set format.
|
||||
*
|
||||
* @param range The range (+ or -) that the accelerometer will measure.
|
||||
*/
|
||||
private void init(Range range) {
|
||||
m_spi.setClockRate(500000);
|
||||
m_spi.setMSBFirst();
|
||||
m_spi.setSampleDataOnFalling();
|
||||
m_spi.setClockActiveLow();
|
||||
m_spi.setChipSelectActiveHigh();
|
||||
|
||||
// Turn on the measurements
|
||||
byte[] commands = new byte[2];
|
||||
commands[0] = kPowerCtlRegister;
|
||||
commands[1] = kPowerCtl_Measure;
|
||||
m_spi.write(commands, 2);
|
||||
|
||||
setRange(range);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_ADXL345, tInstances.kADXL345_SPI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(Range range) {
|
||||
final byte value;
|
||||
|
||||
switch (range) {
|
||||
case k2G:
|
||||
value = 0;
|
||||
break;
|
||||
case k4G:
|
||||
value = 1;
|
||||
break;
|
||||
case k8G:
|
||||
value = 2;
|
||||
break;
|
||||
case k16G:
|
||||
value = 3;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(range + " unsupported");
|
||||
}
|
||||
|
||||
// Specify the data format to read
|
||||
byte[] commands = new byte[]{kDataFormatRegister, (byte) (kDataFormat_FullRes | value)};
|
||||
m_spi.write(commands, commands.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return getAcceleration(Axes.kX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return getAcceleration(Axes.kY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ() {
|
||||
return getAcceleration(Axes.kZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the acceleration of one axis in Gs.
|
||||
*
|
||||
* @param axis The axis to read from.
|
||||
* @return Acceleration of the ADXL345 in Gs.
|
||||
*/
|
||||
public double getAcceleration(ADXL345_SPI.Axes axis) {
|
||||
ByteBuffer transferBuffer = ByteBuffer.allocateDirect(3);
|
||||
transferBuffer.put(0,
|
||||
(byte) ((kAddress_Read | kAddress_MultiByte | kDataRegister) + axis.value));
|
||||
m_spi.transaction(transferBuffer, transferBuffer, 3);
|
||||
// Sensor is little endian
|
||||
transferBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
return transferBuffer.getShort(1) * kGsPerLSB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the acceleration of all axes in Gs.
|
||||
*
|
||||
* @return An object containing the acceleration measured on each axis of the ADXL345 in Gs.
|
||||
*/
|
||||
public ADXL345_SPI.AllAxes getAccelerations() {
|
||||
ADXL345_SPI.AllAxes data = new ADXL345_SPI.AllAxes();
|
||||
if (m_spi != null) {
|
||||
ByteBuffer dataBuffer = ByteBuffer.allocateDirect(7);
|
||||
// Select the data address.
|
||||
dataBuffer.put(0, (byte) (kAddress_Read | kAddress_MultiByte | kDataRegister));
|
||||
m_spi.transaction(dataBuffer, dataBuffer, 7);
|
||||
// Sensor is little endian... swap bytes
|
||||
dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
data.XAxis = dataBuffer.getShort(1) * kGsPerLSB;
|
||||
data.YAxis = dataBuffer.getShort(3) * kGsPerLSB;
|
||||
data.ZAxis = dataBuffer.getShort(5) * kGsPerLSB;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "3AxisAccelerometer";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("X", getX());
|
||||
m_table.putNumber("Y", getY());
|
||||
m_table.putNumber("Z", getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
240
wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java
Normal file
240
wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXL362.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.interfaces.Accelerometer;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* ADXL362 SPI Accelerometer.
|
||||
*
|
||||
* <p>This class allows access to an Analog Devices ADXL362 3-axis accelerometer.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedPrivateField")
|
||||
public class ADXL362 extends SensorBase implements Accelerometer, LiveWindowSendable {
|
||||
private static final byte kRegWrite = 0x0A;
|
||||
private static final byte kRegRead = 0x0B;
|
||||
|
||||
private static final byte kPartIdRegister = 0x02;
|
||||
private static final byte kDataRegister = 0x0E;
|
||||
private static final byte kFilterCtlRegister = 0x2C;
|
||||
private static final byte kPowerCtlRegister = 0x2D;
|
||||
|
||||
private static final byte kFilterCtl_Range2G = 0x00;
|
||||
private static final byte kFilterCtl_Range4G = 0x40;
|
||||
private static final byte kFilterCtl_Range8G = (byte) 0x80;
|
||||
private static final byte kFilterCtl_ODR_100Hz = 0x03;
|
||||
|
||||
private static final byte kPowerCtl_UltraLowNoise = 0x20;
|
||||
private static final byte kPowerCtl_AutoSleep = 0x04;
|
||||
private static final byte kPowerCtl_Measure = 0x02;
|
||||
|
||||
public enum Axes {
|
||||
kX((byte) 0x00),
|
||||
kY((byte) 0x02),
|
||||
kZ((byte) 0x04);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final byte value;
|
||||
|
||||
private Axes(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public static class AllAxes {
|
||||
public double XAxis;
|
||||
public double YAxis;
|
||||
public double ZAxis;
|
||||
}
|
||||
|
||||
private SPI m_spi;
|
||||
private double m_gsPerLSB;
|
||||
|
||||
/**
|
||||
* Constructor. Uses the onboard CS1.
|
||||
*
|
||||
* @param range The range (+ or -) that the accelerometer will measure.
|
||||
*/
|
||||
public ADXL362(Range range) {
|
||||
this(SPI.Port.kOnboardCS1, range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param port The SPI port that the accelerometer is connected to
|
||||
* @param range The range (+ or -) that the accelerometer will measure.
|
||||
*/
|
||||
public ADXL362(SPI.Port port, Range range) {
|
||||
m_spi = new SPI(port);
|
||||
m_spi.setClockRate(3000000);
|
||||
m_spi.setMSBFirst();
|
||||
m_spi.setSampleDataOnFalling();
|
||||
m_spi.setClockActiveLow();
|
||||
m_spi.setChipSelectActiveLow();
|
||||
|
||||
// Validate the part ID
|
||||
ByteBuffer transferBuffer = ByteBuffer.allocateDirect(3);
|
||||
transferBuffer.put(0, kRegRead);
|
||||
transferBuffer.put(1, kPartIdRegister);
|
||||
m_spi.transaction(transferBuffer, transferBuffer, 3);
|
||||
if (transferBuffer.get(2) != (byte) 0xF2) {
|
||||
m_spi.free();
|
||||
m_spi = null;
|
||||
DriverStation.reportError("could not find ADXL362 on SPI port " + port.value, false);
|
||||
return;
|
||||
}
|
||||
|
||||
setRange(range);
|
||||
|
||||
// Turn on the measurements
|
||||
transferBuffer.put(0, kRegWrite);
|
||||
transferBuffer.put(1, kPowerCtlRegister);
|
||||
transferBuffer.put(2, (byte) (kPowerCtl_Measure | kPowerCtl_UltraLowNoise));
|
||||
m_spi.write(transferBuffer, 3);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_ADXL362, port.value);
|
||||
LiveWindow.addSensor("ADXL362", port.value, this);
|
||||
}
|
||||
|
||||
public void free() {
|
||||
m_spi.free();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(Range range) {
|
||||
final byte value;
|
||||
|
||||
switch (range) {
|
||||
case k2G:
|
||||
value = kFilterCtl_Range2G;
|
||||
m_gsPerLSB = 0.001;
|
||||
break;
|
||||
case k4G:
|
||||
value = kFilterCtl_Range4G;
|
||||
m_gsPerLSB = 0.002;
|
||||
break;
|
||||
case k8G:
|
||||
case k16G: // 16G not supported; treat as 8G
|
||||
value = kFilterCtl_Range8G;
|
||||
m_gsPerLSB = 0.004;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(range + " unsupported");
|
||||
|
||||
}
|
||||
|
||||
// Specify the data format to read
|
||||
byte[] commands = new byte[]{kRegWrite, kFilterCtlRegister, (byte) (kFilterCtl_ODR_100Hz
|
||||
| value)};
|
||||
m_spi.write(commands, commands.length);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return getAcceleration(Axes.kX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return getAcceleration(Axes.kY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZ() {
|
||||
return getAcceleration(Axes.kZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the acceleration of one axis in Gs.
|
||||
*
|
||||
* @param axis The axis to read from.
|
||||
* @return Acceleration of the ADXL362 in Gs.
|
||||
*/
|
||||
public double getAcceleration(ADXL362.Axes axis) {
|
||||
if (m_spi == null) {
|
||||
return 0.0;
|
||||
}
|
||||
ByteBuffer transferBuffer = ByteBuffer.allocateDirect(4);
|
||||
transferBuffer.put(0, kRegRead);
|
||||
transferBuffer.put(1, (byte) (kDataRegister + axis.value));
|
||||
m_spi.transaction(transferBuffer, transferBuffer, 4);
|
||||
// Sensor is little endian
|
||||
transferBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
return transferBuffer.getShort(2) * m_gsPerLSB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the acceleration of all axes in Gs.
|
||||
*
|
||||
* @return An object containing the acceleration measured on each axis of the ADXL362 in Gs.
|
||||
*/
|
||||
public ADXL362.AllAxes getAccelerations() {
|
||||
ADXL362.AllAxes data = new ADXL362.AllAxes();
|
||||
if (m_spi != null) {
|
||||
ByteBuffer dataBuffer = ByteBuffer.allocateDirect(8);
|
||||
// Select the data address.
|
||||
dataBuffer.put(0, kRegRead);
|
||||
dataBuffer.put(1, kDataRegister);
|
||||
m_spi.transaction(dataBuffer, dataBuffer, 8);
|
||||
// Sensor is little endian... swap bytes
|
||||
dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
data.XAxis = dataBuffer.getShort(2) * m_gsPerLSB;
|
||||
data.YAxis = dataBuffer.getShort(4) * m_gsPerLSB;
|
||||
data.ZAxis = dataBuffer.getShort(6) * m_gsPerLSB;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "3AxisAccelerometer";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("X", getX());
|
||||
m_table.putNumber("Y", getY());
|
||||
m_table.putNumber("Z", getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
161
wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java
Normal file
161
wpilibj/src/main/java/edu/wpi/first/wpilibj/ADXRS450_Gyro.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2017. 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.interfaces.Gyro;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
|
||||
/**
|
||||
* Use a rate gyro to return the robots heading relative to a starting position. The Gyro class
|
||||
* tracks the robots heading based on the starting position. As the robot rotates the new heading is
|
||||
* computed by integrating the rate of rotation returned by the sensor. When the class is
|
||||
* instantiated, it does a short calibration routine where it samples the gyro while at rest to
|
||||
* determine the default offset. This is subtracted from each sample to determine the heading.
|
||||
*
|
||||
* <p>This class is for the digital ADXRS450 gyro sensor that connects via SPI.
|
||||
*/
|
||||
@SuppressWarnings({"TypeName", "AbbreviationAsWordInName", "PMD.UnusedPrivateField"})
|
||||
public class ADXRS450_Gyro extends GyroBase implements Gyro, PIDSource, LiveWindowSendable {
|
||||
private static final double kSamplePeriod = 0.001;
|
||||
private static final double kCalibrationSampleTime = 5.0;
|
||||
private static final double kDegreePerSecondPerLSB = 0.0125;
|
||||
|
||||
private static final int kRateRegister = 0x00;
|
||||
private static final int kTemRegister = 0x02;
|
||||
private static final int kLoCSTRegister = 0x04;
|
||||
private static final int kHiCSTRegister = 0x06;
|
||||
private static final int kQuadRegister = 0x08;
|
||||
private static final int kFaultRegister = 0x0A;
|
||||
private static final int kPIDRegister = 0x0C;
|
||||
private static final int kSNHighRegister = 0x0E;
|
||||
private static final int kSNLowRegister = 0x10;
|
||||
|
||||
private SPI m_spi;
|
||||
|
||||
/**
|
||||
* Constructor. Uses the onboard CS0.
|
||||
*/
|
||||
public ADXRS450_Gyro() {
|
||||
this(SPI.Port.kOnboardCS0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param port The SPI port that the gyro is connected to
|
||||
*/
|
||||
public ADXRS450_Gyro(SPI.Port port) {
|
||||
m_spi = new SPI(port);
|
||||
m_spi.setClockRate(3000000);
|
||||
m_spi.setMSBFirst();
|
||||
m_spi.setSampleDataOnRising();
|
||||
m_spi.setClockActiveHigh();
|
||||
m_spi.setChipSelectActiveLow();
|
||||
|
||||
// Validate the part ID
|
||||
if ((readRegister(kPIDRegister) & 0xff00) != 0x5200) {
|
||||
m_spi.free();
|
||||
m_spi = null;
|
||||
DriverStation.reportError("could not find ADXRS450 gyro on SPI port " + port.value,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_spi.initAccumulator(kSamplePeriod, 0x20000000, 4, 0x0c00000e, 0x04000000, 10, 16,
|
||||
true, true);
|
||||
|
||||
calibrate();
|
||||
|
||||
HAL.report(tResourceType.kResourceType_ADXRS450, port.value);
|
||||
LiveWindow.addSensor("ADXRS450_Gyro", port.value, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void calibrate() {
|
||||
if (m_spi == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Timer.delay(0.1);
|
||||
|
||||
m_spi.setAccumulatorCenter(0);
|
||||
m_spi.resetAccumulator();
|
||||
|
||||
Timer.delay(kCalibrationSampleTime);
|
||||
|
||||
m_spi.setAccumulatorCenter((int) m_spi.getAccumulatorAverage());
|
||||
m_spi.resetAccumulator();
|
||||
}
|
||||
|
||||
private boolean calcParity(int value) {
|
||||
boolean parity = false;
|
||||
while (value != 0) {
|
||||
parity = !parity;
|
||||
value = value & (value - 1);
|
||||
}
|
||||
return parity;
|
||||
}
|
||||
|
||||
private int readRegister(int reg) {
|
||||
int cmdhi = 0x8000 | (reg << 1);
|
||||
boolean parity = calcParity(cmdhi);
|
||||
|
||||
ByteBuffer buf = ByteBuffer.allocateDirect(4);
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
buf.put(0, (byte) (cmdhi >> 8));
|
||||
buf.put(1, (byte) (cmdhi & 0xff));
|
||||
buf.put(2, (byte) 0);
|
||||
buf.put(3, (byte) (parity ? 0 : 1));
|
||||
|
||||
m_spi.write(buf, 4);
|
||||
m_spi.read(false, buf, 4);
|
||||
|
||||
if ((buf.get(0) & 0xe0) == 0) {
|
||||
return 0; // error, return 0
|
||||
}
|
||||
return (buf.getInt(0) >> 5) & 0xffff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
m_spi.resetAccumulator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete (free) the spi port used for the gyro and stop accumulating.
|
||||
*/
|
||||
@Override
|
||||
public void free() {
|
||||
if (m_spi != null) {
|
||||
m_spi.free();
|
||||
m_spi = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAngle() {
|
||||
if (m_spi == null) {
|
||||
return 0.0;
|
||||
}
|
||||
return m_spi.getAccumulatorValue() * kDegreePerSecondPerLSB * kSamplePeriod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRate() {
|
||||
if (m_spi == null) {
|
||||
return 0.0;
|
||||
}
|
||||
return m_spi.getAccumulatorLastValue() * kDegreePerSecondPerLSB;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* Structure for holding the values stored in an accumulator.
|
||||
*/
|
||||
public class AccumulatorResult {
|
||||
|
||||
/**
|
||||
* The total value accumulated.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public long value;
|
||||
/**
|
||||
* The number of sample vaule was accumulated over.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public long count;
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Handle operation of an analog accelerometer. The accelerometer reads acceleration directly
|
||||
* through the sensor. Many sensors have multiple axis and can be treated as multiple devices. Each
|
||||
* is calibrated by finding the center value over a period of time.
|
||||
*/
|
||||
public class AnalogAccelerometer extends SensorBase implements PIDSource, LiveWindowSendable {
|
||||
|
||||
private AnalogInput m_analogChannel;
|
||||
private double m_voltsPerG = 1.0;
|
||||
private double m_zeroGVoltage = 2.5;
|
||||
private boolean m_allocatedChannel;
|
||||
protected PIDSourceType m_pidSource = PIDSourceType.kDisplacement;
|
||||
|
||||
/**
|
||||
* Common initialization.
|
||||
*/
|
||||
private void initAccelerometer() {
|
||||
HAL.report(tResourceType.kResourceType_Accelerometer,
|
||||
m_analogChannel.getChannel());
|
||||
LiveWindow.addSensor("Accelerometer", m_analogChannel.getChannel(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of an accelerometer.
|
||||
*
|
||||
* <p>The constructor allocates desired analog channel.
|
||||
*
|
||||
* @param channel The channel number for the analog input the accelerometer is connected to
|
||||
*/
|
||||
public AnalogAccelerometer(final int channel) {
|
||||
m_analogChannel = new AnalogInput(channel);
|
||||
m_allocatedChannel = true;
|
||||
initAccelerometer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of Accelerometer from an existing AnalogChannel. Make a new instance of
|
||||
* accelerometer given an AnalogChannel. This is particularly useful if the port is going to be
|
||||
* read as an analog channel as well as through the Accelerometer class.
|
||||
*
|
||||
* @param channel The existing AnalogInput object for the analog input the accelerometer is
|
||||
* connected to
|
||||
*/
|
||||
public AnalogAccelerometer(AnalogInput channel) {
|
||||
requireNonNull(channel, "Analog Channel given was null");
|
||||
|
||||
m_allocatedChannel = false;
|
||||
m_analogChannel = channel;
|
||||
initAccelerometer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the analog components used for the accelerometer.
|
||||
*/
|
||||
@Override
|
||||
public void free() {
|
||||
if (m_analogChannel != null && m_allocatedChannel) {
|
||||
m_analogChannel.free();
|
||||
}
|
||||
m_analogChannel = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the acceleration in Gs.
|
||||
*
|
||||
* <p>The acceleration is returned units of Gs.
|
||||
*
|
||||
* @return The current acceleration of the sensor in Gs.
|
||||
*/
|
||||
public double getAcceleration() {
|
||||
return (m_analogChannel.getAverageVoltage() - m_zeroGVoltage) / m_voltsPerG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the accelerometer sensitivity.
|
||||
*
|
||||
* <p>This sets the sensitivity of the accelerometer used for calculating the acceleration. The
|
||||
* sensitivity varies by accelerometer model. There are constants defined for various models.
|
||||
*
|
||||
* @param sensitivity The sensitivity of accelerometer in Volts per G.
|
||||
*/
|
||||
public void setSensitivity(double sensitivity) {
|
||||
m_voltsPerG = sensitivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the voltage that corresponds to 0 G.
|
||||
*
|
||||
* <p>The zero G voltage varies by accelerometer model. There are constants defined for various
|
||||
* models.
|
||||
*
|
||||
* @param zero The zero G voltage.
|
||||
*/
|
||||
public void setZero(double zero) {
|
||||
m_zeroGVoltage = zero;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
m_pidSource = pidSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return m_pidSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Acceleration for the PID Source parent.
|
||||
*
|
||||
* @return The current acceleration in Gs.
|
||||
*/
|
||||
@Override
|
||||
public double pidGet() {
|
||||
return getAcceleration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Accelerometer";
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", getAcceleration());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analog Channels don't have to do anything special when entering the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Analog Channels don't have to do anything special when exiting the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
189
wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java
Normal file
189
wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogGyro.java
Normal file
@@ -0,0 +1,189 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.AnalogGyroJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.interfaces.Gyro;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Use a rate gyro to return the robots heading relative to a starting position. The Gyro class
|
||||
* tracks the robots heading based on the starting position. As the robot rotates the new heading is
|
||||
* computed by integrating the rate of rotation returned by the sensor. When the class is
|
||||
* instantiated, it does a short calibration routine where it samples the gyro while at rest to
|
||||
* determine the default offset. This is subtracted from each sample to determine the heading.
|
||||
*
|
||||
* <p>This class is for gyro sensors that connect to an analog input.
|
||||
*/
|
||||
public class AnalogGyro extends GyroBase implements Gyro, PIDSource, LiveWindowSendable {
|
||||
|
||||
private static final double kDefaultVoltsPerDegreePerSecond = 0.007;
|
||||
protected AnalogInput m_analog;
|
||||
private boolean m_channelAllocated = false;
|
||||
|
||||
private int m_gyroHandle = 0;
|
||||
|
||||
/**
|
||||
* Initialize the gyro. Calibration is handled by calibrate().
|
||||
*/
|
||||
public void initGyro() {
|
||||
if (m_gyroHandle == 0) {
|
||||
m_gyroHandle = AnalogGyroJNI.initializeAnalogGyro(m_analog.m_port);
|
||||
}
|
||||
|
||||
AnalogGyroJNI.setupAnalogGyro(m_gyroHandle);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_Gyro, m_analog.getChannel());
|
||||
LiveWindow.addSensor("AnalogGyro", m_analog.getChannel(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void calibrate() {
|
||||
AnalogGyroJNI.calibrateAnalogGyro(m_gyroHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gyro constructor using the channel number.
|
||||
*
|
||||
* @param channel The analog channel the gyro is connected to. Gyros can only be used on on-board
|
||||
* channels 0-1.
|
||||
*/
|
||||
public AnalogGyro(int channel) {
|
||||
this(new AnalogInput(channel));
|
||||
m_channelAllocated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gyro constructor with a precreated analog channel object. Use this constructor when the analog
|
||||
* channel needs to be shared.
|
||||
*
|
||||
* @param channel The AnalogInput object that the gyro is connected to. Gyros can only be used on
|
||||
* on-board channels 0-1.
|
||||
*/
|
||||
public AnalogGyro(AnalogInput channel) {
|
||||
requireNonNull(channel, "AnalogInput supplied to Gyro constructor is null");
|
||||
|
||||
m_analog = channel;
|
||||
initGyro();
|
||||
calibrate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gyro constructor using the channel number along with parameters for presetting the center and
|
||||
* offset values. Bypasses calibration.
|
||||
*
|
||||
* @param channel The analog channel the gyro is connected to. Gyros can only be used on on-board
|
||||
* channels 0-1.
|
||||
* @param center Preset uncalibrated value to use as the accumulator center value.
|
||||
* @param offset Preset uncalibrated value to use as the gyro offset.
|
||||
*/
|
||||
public AnalogGyro(int channel, int center, double offset) {
|
||||
this(new AnalogInput(channel), center, offset);
|
||||
m_channelAllocated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gyro constructor with a precreated analog channel object along with parameters for presetting
|
||||
* the center and offset values. Bypasses calibration.
|
||||
*
|
||||
* @param channel The analog channel the gyro is connected to. Gyros can only be used on on-board
|
||||
* channels 0-1.
|
||||
* @param center Preset uncalibrated value to use as the accumulator center value.
|
||||
* @param offset Preset uncalibrated value to use as the gyro offset.
|
||||
*/
|
||||
public AnalogGyro(AnalogInput channel, int center, double offset) {
|
||||
requireNonNull(channel, "AnalogInput supplied to Gyro constructor is null");
|
||||
|
||||
m_analog = channel;
|
||||
initGyro();
|
||||
AnalogGyroJNI.setAnalogGyroParameters(m_gyroHandle, kDefaultVoltsPerDegreePerSecond,
|
||||
offset, center);
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
AnalogGyroJNI.resetAnalogGyro(m_gyroHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete (free) the accumulator and the analog components used for the gyro.
|
||||
*/
|
||||
@Override
|
||||
public void free() {
|
||||
if (m_analog != null && m_channelAllocated) {
|
||||
m_analog.free();
|
||||
}
|
||||
m_analog = null;
|
||||
AnalogGyroJNI.freeAnalogGyro(m_gyroHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAngle() {
|
||||
if (m_analog == null) {
|
||||
return 0.0;
|
||||
} else {
|
||||
return AnalogGyroJNI.getAnalogGyroAngle(m_gyroHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRate() {
|
||||
if (m_analog == null) {
|
||||
return 0.0;
|
||||
} else {
|
||||
return AnalogGyroJNI.getAnalogGyroRate(m_gyroHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the gyro offset value set during calibration to use as a future preset.
|
||||
*
|
||||
* @return the current offset value
|
||||
*/
|
||||
public double getOffset() {
|
||||
return AnalogGyroJNI.getAnalogGyroOffset(m_gyroHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the gyro center value set during calibration to use as a future preset.
|
||||
*
|
||||
* @return the current center value
|
||||
*/
|
||||
public int getCenter() {
|
||||
return AnalogGyroJNI.getAnalogGyroCenter(m_gyroHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the gyro sensitivity. This takes the number of volts/degree/second sensitivity of the gyro
|
||||
* and uses it in subsequent calculations to allow the code to work with multiple gyros. This
|
||||
* value is typically found in the gyro datasheet.
|
||||
*
|
||||
* @param voltsPerDegreePerSecond The sensitivity in Volts/degree/second.
|
||||
*/
|
||||
public void setSensitivity(double voltsPerDegreePerSecond) {
|
||||
AnalogGyroJNI.setAnalogGyroVoltsPerDegreePerSecond(m_gyroHandle,
|
||||
voltsPerDegreePerSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the size of the neutral zone. Any voltage from the gyro less than this amount from the
|
||||
* center is considered stationary. Setting a deadband will decrease the amount of drift when the
|
||||
* gyro isn't rotating, but will make it less accurate.
|
||||
*
|
||||
* @param volts The size of the deadband in volts
|
||||
*/
|
||||
void setDeadband(double volts) {
|
||||
AnalogGyroJNI.setAnalogGyroDeadband(m_gyroHandle, volts);
|
||||
}
|
||||
}
|
||||
400
wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogInput.java
Normal file
400
wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogInput.java
Normal file
@@ -0,0 +1,400 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.AnalogJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.util.AllocationException;
|
||||
|
||||
/**
|
||||
* Analog channel class.
|
||||
*
|
||||
* <p>Each analog channel is read from hardware as a 12-bit number representing 0V to 5V.
|
||||
*
|
||||
* <p>Connected to each analog channel is an averaging and oversampling engine. This engine
|
||||
* accumulates the specified ( by setAverageBits() and setOversampleBits() ) number of samples
|
||||
* before returning a new value. This is not a sliding window average. The only difference between
|
||||
* the oversampled samples and the averaged samples is that the oversampled samples are simply
|
||||
* accumulated effectively increasing the resolution, while the averaged samples are divided by the
|
||||
* number of samples to retain the resolution, but get more stable values.
|
||||
*/
|
||||
public class AnalogInput extends SensorBase implements PIDSource, LiveWindowSendable {
|
||||
|
||||
private static final int kAccumulatorSlot = 1;
|
||||
int m_port; // explicit no modifier, private and package accessable.
|
||||
private int m_channel;
|
||||
private static final int[] kAccumulatorChannels = {0, 1};
|
||||
private long m_accumulatorOffset;
|
||||
protected PIDSourceType m_pidSource = PIDSourceType.kDisplacement;
|
||||
|
||||
/**
|
||||
* Construct an analog channel.
|
||||
*
|
||||
* @param channel The channel number to represent. 0-3 are on-board 4-7 are on the MXP port.
|
||||
*/
|
||||
public AnalogInput(final int channel) {
|
||||
checkAnalogInputChannel(channel);
|
||||
m_channel = channel;
|
||||
|
||||
final int portHandle = AnalogJNI.getPort((byte) channel);
|
||||
m_port = AnalogJNI.initializeAnalogInputPort(portHandle);
|
||||
|
||||
LiveWindow.addSensor("AnalogInput", channel, this);
|
||||
HAL.report(tResourceType.kResourceType_AnalogChannel, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel destructor.
|
||||
*/
|
||||
public void free() {
|
||||
AnalogJNI.freeAnalogInputPort(m_port);
|
||||
m_port = 0;
|
||||
m_channel = 0;
|
||||
m_accumulatorOffset = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sample straight from this channel. The sample is a 12-bit value representing the 0V to 5V
|
||||
* range of the A/D converter. The units are in A/D converter codes. Use GetVoltage() to get the
|
||||
* analog value in calibrated units.
|
||||
*
|
||||
* @return A sample straight from this channel.
|
||||
*/
|
||||
public int getValue() {
|
||||
return AnalogJNI.getAnalogValue(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sample from the output of the oversample and average engine for this channel. The sample
|
||||
* is 12-bit + the bits configured in SetOversampleBits(). The value configured in
|
||||
* setAverageBits() will cause this value to be averaged 2^bits number of samples. This is not a
|
||||
* sliding window. The sample will not change until 2^(OversampleBits + AverageBits) samples have
|
||||
* been acquired from this channel. Use getAverageVoltage() to get the analog value in calibrated
|
||||
* units.
|
||||
*
|
||||
* @return A sample from the oversample and average engine for this channel.
|
||||
*/
|
||||
public int getAverageValue() {
|
||||
return AnalogJNI.getAnalogAverageValue(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a scaled sample straight from this channel. The value is scaled to units of Volts using the
|
||||
* calibrated scaling data from getLSBWeight() and getOffset().
|
||||
*
|
||||
* @return A scaled sample straight from this channel.
|
||||
*/
|
||||
public double getVoltage() {
|
||||
return AnalogJNI.getAnalogVoltage(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a scaled sample from the output of the oversample and average engine for this channel. The
|
||||
* value is scaled to units of Volts using the calibrated scaling data from getLSBWeight() and
|
||||
* getOffset(). Using oversampling will cause this value to be higher resolution, but it will
|
||||
* update more slowly. Using averaging will cause this value to be more stable, but it will update
|
||||
* more slowly.
|
||||
*
|
||||
* @return A scaled sample from the output of the oversample and average engine for this channel.
|
||||
*/
|
||||
public double getAverageVoltage() {
|
||||
return AnalogJNI.getAnalogAverageVoltage(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factory scaling least significant bit weight constant. The least significant bit weight
|
||||
* constant for the channel that was calibrated in manufacturing and stored in an eeprom.
|
||||
*
|
||||
* <p>Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
|
||||
*
|
||||
* @return Least significant bit weight.
|
||||
*/
|
||||
public long getLSBWeight() {
|
||||
return AnalogJNI.getAnalogLSBWeight(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factory scaling offset constant. The offset constant for the channel that was
|
||||
* calibrated in manufacturing and stored in an eeprom.
|
||||
*
|
||||
* <p>Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9)
|
||||
*
|
||||
* @return Offset constant.
|
||||
*/
|
||||
public int getOffset() {
|
||||
return AnalogJNI.getAnalogOffset(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channel number.
|
||||
*
|
||||
* @return The channel number.
|
||||
*/
|
||||
public int getChannel() {
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of averaging bits. This sets the number of averaging bits. The actual number of
|
||||
* averaged samples is 2^bits. The averaging is done automatically in the FPGA.
|
||||
*
|
||||
* @param bits The number of averaging bits.
|
||||
*/
|
||||
public void setAverageBits(final int bits) {
|
||||
AnalogJNI.setAnalogAverageBits(m_port, bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of averaging bits. This gets the number of averaging bits from the FPGA. The
|
||||
* actual number of averaged samples is 2^bits. The averaging is done automatically in the FPGA.
|
||||
*
|
||||
* @return The number of averaging bits.
|
||||
*/
|
||||
public int getAverageBits() {
|
||||
return AnalogJNI.getAnalogAverageBits(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of oversample bits. This sets the number of oversample bits. The actual number
|
||||
* of oversampled values is 2^bits. The oversampling is done automatically in the FPGA.
|
||||
*
|
||||
* @param bits The number of oversample bits.
|
||||
*/
|
||||
public void setOversampleBits(final int bits) {
|
||||
AnalogJNI.setAnalogOversampleBits(m_port, bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of oversample bits. This gets the number of oversample bits from the FPGA. The
|
||||
* actual number of oversampled values is 2^bits. The oversampling is done automatically in the
|
||||
* FPGA.
|
||||
*
|
||||
* @return The number of oversample bits.
|
||||
*/
|
||||
public int getOversampleBits() {
|
||||
return AnalogJNI.getAnalogOversampleBits(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the accumulator.
|
||||
*/
|
||||
public void initAccumulator() {
|
||||
if (!isAccumulatorChannel()) {
|
||||
throw new AllocationException("Accumulators are only available on slot " + kAccumulatorSlot
|
||||
+ " on channels " + kAccumulatorChannels[0] + ", " + kAccumulatorChannels[1]);
|
||||
}
|
||||
m_accumulatorOffset = 0;
|
||||
AnalogJNI.initAccumulator(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an initial value for the accumulator.
|
||||
*
|
||||
* <p>This will be added to all values returned to the user.
|
||||
*
|
||||
* @param initialValue The value that the accumulator should start from when reset.
|
||||
*/
|
||||
public void setAccumulatorInitialValue(long initialValue) {
|
||||
m_accumulatorOffset = initialValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the accumulator to the initial value.
|
||||
*/
|
||||
public void resetAccumulator() {
|
||||
AnalogJNI.resetAccumulator(m_port);
|
||||
|
||||
// Wait until the next sample, so the next call to getAccumulator*()
|
||||
// won't have old values.
|
||||
final double sampleTime = 1.0 / getGlobalSampleRate();
|
||||
final double overSamples = 1 << getOversampleBits();
|
||||
final double averageSamples = 1 << getAverageBits();
|
||||
Timer.delay(sampleTime * overSamples * averageSamples);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the center value of the accumulator.
|
||||
*
|
||||
* <p>The center value is subtracted from each A/D value before it is added to the accumulator.
|
||||
* This is used for the center value of devices like gyros and accelerometers to take the device
|
||||
* offset into account when integrating.
|
||||
*
|
||||
* <p>This center value is based on the output of the oversampled and averaged source the
|
||||
* accumulator channel. Because of this, any non-zero oversample bits will affect the size of the
|
||||
* value for this field.
|
||||
*/
|
||||
public void setAccumulatorCenter(int center) {
|
||||
AnalogJNI.setAccumulatorCenter(m_port, center);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the accumulator's deadband.
|
||||
*
|
||||
* @param deadband The deadband size in ADC codes (12-bit value)
|
||||
*/
|
||||
public void setAccumulatorDeadband(int deadband) {
|
||||
AnalogJNI.setAccumulatorDeadband(m_port, deadband);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the accumulated value.
|
||||
*
|
||||
* <p>Read the value that has been accumulating. The accumulator is attached after the oversample
|
||||
* and average engine.
|
||||
*
|
||||
* @return The 64-bit value accumulated since the last Reset().
|
||||
*/
|
||||
public long getAccumulatorValue() {
|
||||
return AnalogJNI.getAccumulatorValue(m_port) + m_accumulatorOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the number of accumulated values.
|
||||
*
|
||||
* <p>Read the count of the accumulated values since the accumulator was last Reset().
|
||||
*
|
||||
* @return The number of times samples from the channel were accumulated.
|
||||
*/
|
||||
public long getAccumulatorCount() {
|
||||
return AnalogJNI.getAccumulatorCount(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the accumulated value and the number of accumulated values atomically.
|
||||
*
|
||||
* <p>This function reads the value and count from the FPGA atomically. This can be used for
|
||||
* averaging.
|
||||
*
|
||||
* @param result AccumulatorResult object to store the results in.
|
||||
*/
|
||||
public void getAccumulatorOutput(AccumulatorResult result) {
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException("Null parameter `result'");
|
||||
}
|
||||
if (!isAccumulatorChannel()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Channel " + m_channel + " is not an accumulator channel.");
|
||||
}
|
||||
ByteBuffer value = ByteBuffer.allocateDirect(8);
|
||||
// set the byte order
|
||||
value.order(ByteOrder.LITTLE_ENDIAN);
|
||||
ByteBuffer count = ByteBuffer.allocateDirect(8);
|
||||
// set the byte order
|
||||
count.order(ByteOrder.LITTLE_ENDIAN);
|
||||
AnalogJNI.getAccumulatorOutput(m_port, value.asLongBuffer(), count.asLongBuffer());
|
||||
result.value = value.asLongBuffer().get(0) + m_accumulatorOffset;
|
||||
result.count = count.asLongBuffer().get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the channel attached to an accumulator.
|
||||
*
|
||||
* @return The analog channel is attached to an accumulator.
|
||||
*/
|
||||
public boolean isAccumulatorChannel() {
|
||||
for (int i = 0; i < kAccumulatorChannels.length; i++) {
|
||||
if (m_channel == kAccumulatorChannels[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sample rate per channel.
|
||||
*
|
||||
* <p>This is a global setting for all channels. The maximum rate is 500kS/s divided by the number
|
||||
* of channels in use. This is 62500 samples/s per channel if all 8 channels are used.
|
||||
*
|
||||
* @param samplesPerSecond The number of samples per second.
|
||||
*/
|
||||
public static void setGlobalSampleRate(final double samplesPerSecond) {
|
||||
AnalogJNI.setAnalogSampleRate(samplesPerSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current sample rate.
|
||||
*
|
||||
* <p>This assumes one entry in the scan list. This is a global setting for all channels.
|
||||
*
|
||||
* @return Sample rate.
|
||||
*/
|
||||
public static double getGlobalSampleRate() {
|
||||
return AnalogJNI.getAnalogSampleRate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
m_pidSource = pidSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return m_pidSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the average voltage for use with PIDController.
|
||||
*
|
||||
* @return the average voltage
|
||||
*/
|
||||
@Override
|
||||
public double pidGet() {
|
||||
return getAverageVoltage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Analog Input";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", getAverageVoltage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analog Channels don't have to do anything special when entering the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Analog Channels don't have to do anything special when exiting the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
105
wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogOutput.java
Normal file
105
wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogOutput.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014-2017. 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.hal.AnalogJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* Analog output class.
|
||||
*/
|
||||
public class AnalogOutput extends SensorBase implements LiveWindowSendable {
|
||||
private int m_port;
|
||||
private int m_channel;
|
||||
|
||||
/**
|
||||
* Construct an analog output on a specified MXP channel.
|
||||
*
|
||||
* @param channel The channel number to represent.
|
||||
*/
|
||||
public AnalogOutput(final int channel) {
|
||||
checkAnalogOutputChannel(channel);
|
||||
m_channel = channel;
|
||||
|
||||
final int portHandle = AnalogJNI.getPort((byte) channel);
|
||||
m_port = AnalogJNI.initializeAnalogOutputPort(portHandle);
|
||||
|
||||
LiveWindow.addSensor("AnalogOutput", channel, this);
|
||||
HAL.report(tResourceType.kResourceType_AnalogOutput, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel destructor.
|
||||
*/
|
||||
public void free() {
|
||||
AnalogJNI.freeAnalogOutputPort(m_port);
|
||||
m_port = 0;
|
||||
m_channel = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channel of this AnalogOutput.
|
||||
*/
|
||||
public int getChannel() {
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
public void setVoltage(double voltage) {
|
||||
AnalogJNI.setAnalogOutput(m_port, voltage);
|
||||
}
|
||||
|
||||
public double getVoltage() {
|
||||
return AnalogJNI.getAnalogOutput(m_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Analog Output";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", getVoltage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analog Channels don't have to do anything special when entering the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Analog Channels don't have to do anything special when exiting the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.interfaces.Potentiometer;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* Class for reading analog potentiometers. Analog potentiometers read in an analog voltage that
|
||||
* corresponds to a position. The position is in whichever units you choose, by way of the scaling
|
||||
* and offset constants passed to the constructor.
|
||||
*/
|
||||
public class AnalogPotentiometer implements Potentiometer, LiveWindowSendable {
|
||||
private AnalogInput m_analogInput;
|
||||
private boolean m_initAnalogInput;
|
||||
private double m_fullRange;
|
||||
private double m_offset;
|
||||
protected PIDSourceType m_pidSource = PIDSourceType.kDisplacement;
|
||||
|
||||
/**
|
||||
* AnalogPotentiometer constructor.
|
||||
*
|
||||
* <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
|
||||
* have a 270 degree potentiometer and you want the output to be degrees with the halfway point as
|
||||
* 0 degrees. The fullRange value is 270.0(degrees) and the offset is -135.0 since the halfway
|
||||
* point after scaling is 135 degrees. This will calculate the result from the fullRange times
|
||||
* the fraction of the supply voltage, plus the offset.
|
||||
*
|
||||
* @param channel The analog channel this potentiometer is plugged into.
|
||||
* @param fullRange The scaling to multiply the fraction by to get a meaningful unit.
|
||||
* @param offset The offset to add to the scaled value for controlling the zero value
|
||||
*/
|
||||
public AnalogPotentiometer(final int channel, double fullRange, double offset) {
|
||||
this(new AnalogInput(channel), fullRange, offset);
|
||||
m_initAnalogInput = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* AnalogPotentiometer constructor.
|
||||
*
|
||||
* <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
|
||||
* have a 270 degree potentiometer and you want the output to be degrees with the halfway point as
|
||||
* 0 degrees. The fullRange value is 270.0(degrees) and the offset is -135.0 since the halfway
|
||||
* point after scaling is 135 degrees. This will calculate the result from the fullRange times
|
||||
* the fraction of the supply voltage, plus the offset.
|
||||
*
|
||||
* @param input The {@link AnalogInput} this potentiometer is plugged into.
|
||||
* @param fullRange The scaling to multiply the fraction by to get a meaningful unit.
|
||||
* @param offset The offset to add to the scaled value for controlling the zero value
|
||||
*/
|
||||
public AnalogPotentiometer(final AnalogInput input, double fullRange, double offset) {
|
||||
m_analogInput = input;
|
||||
m_initAnalogInput = false;
|
||||
|
||||
m_fullRange = fullRange;
|
||||
m_offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* AnalogPotentiometer constructor.
|
||||
*
|
||||
* <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
|
||||
* have a 270 degree potentiometer and you want the output to be degrees with the halfway point as
|
||||
* 0 degrees. The fullRange value is 270.0(degrees) and the offset is -135.0 since the halfway
|
||||
* point after scaling is 135 degrees.
|
||||
*
|
||||
* @param channel The analog channel this potentiometer is plugged into.
|
||||
* @param scale The scaling to multiply the voltage by to get a meaningful unit.
|
||||
*/
|
||||
public AnalogPotentiometer(final int channel, double scale) {
|
||||
this(channel, scale, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* AnalogPotentiometer constructor.
|
||||
*
|
||||
* <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
|
||||
* have a 270 degree potentiometer and you want the output to be degrees with the halfway point as
|
||||
* 0 degrees. The fullRange value is 270.0(degrees) and the offset is -135.0 since the halfway
|
||||
* point after scaling is 135 degrees.
|
||||
*
|
||||
* @param input The {@link AnalogInput} this potentiometer is plugged into.
|
||||
* @param scale The scaling to multiply the voltage by to get a meaningful unit.
|
||||
*/
|
||||
public AnalogPotentiometer(final AnalogInput input, double scale) {
|
||||
this(input, scale, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* AnalogPotentiometer constructor.
|
||||
*
|
||||
* @param channel The analog channel this potentiometer is plugged into.
|
||||
*/
|
||||
public AnalogPotentiometer(final int channel) {
|
||||
this(channel, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* AnalogPotentiometer constructor.
|
||||
*
|
||||
* @param input The {@link AnalogInput} this potentiometer is plugged into.
|
||||
*/
|
||||
public AnalogPotentiometer(final AnalogInput input) {
|
||||
this(input, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current reading of the potentiometer.
|
||||
*
|
||||
* @return The current position of the potentiometer.
|
||||
*/
|
||||
@Override
|
||||
public double get() {
|
||||
return (m_analogInput.getVoltage() / ControllerPower.getVoltage5V()) * m_fullRange + m_offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
if (!pidSource.equals(PIDSourceType.kDisplacement)) {
|
||||
throw new IllegalArgumentException("Only displacement PID is allowed for potentiometers.");
|
||||
}
|
||||
m_pidSource = pidSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return m_pidSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the PIDSource interface.
|
||||
*
|
||||
* @return The current reading.
|
||||
*/
|
||||
@Override
|
||||
public double pidGet() {
|
||||
return get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Analog Input";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees this resource.
|
||||
*/
|
||||
public void free() {
|
||||
if (m_initAnalogInput) {
|
||||
m_analogInput.free();
|
||||
m_analogInput = null;
|
||||
m_initAnalogInput = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analog Channels don't have to do anything special when entering the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Analog Channels don't have to do anything special when exiting the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
176
wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTrigger.java
Normal file
176
wpilibj/src/main/java/edu/wpi/first/wpilibj/AnalogTrigger.java
Normal file
@@ -0,0 +1,176 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import edu.wpi.first.wpilibj.AnalogTriggerOutput.AnalogTriggerType;
|
||||
import edu.wpi.first.wpilibj.hal.AnalogJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.util.BoundaryException;
|
||||
|
||||
/**
|
||||
* Class for creating and configuring Analog Triggers.
|
||||
*/
|
||||
public class AnalogTrigger {
|
||||
|
||||
/**
|
||||
* Exceptions dealing with improper operation of the Analog trigger.
|
||||
*/
|
||||
public class AnalogTriggerException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Create a new exception with the given message.
|
||||
*
|
||||
* @param message the message to pass with the exception
|
||||
*/
|
||||
public AnalogTriggerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Where the analog trigger is attached.
|
||||
*/
|
||||
protected int m_port;
|
||||
protected int m_index;
|
||||
protected AnalogInput m_analogInput = null;
|
||||
protected boolean m_ownsAnalog = false;
|
||||
|
||||
/**
|
||||
* Constructor for an analog trigger given a channel number.
|
||||
*
|
||||
* @param channel the port to use for the analog trigger
|
||||
*/
|
||||
public AnalogTrigger(final int channel) {
|
||||
this(new AnalogInput(channel));
|
||||
m_ownsAnalog = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an analog trigger given an analog channel. This should be used in the case of sharing
|
||||
* an analog channel between the trigger and an analog input object.
|
||||
*
|
||||
* @param channel the AnalogInput to use for the analog trigger
|
||||
*/
|
||||
public AnalogTrigger(AnalogInput channel) {
|
||||
m_analogInput = channel;
|
||||
ByteBuffer index = ByteBuffer.allocateDirect(4);
|
||||
index.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
m_port =
|
||||
AnalogJNI.initializeAnalogTrigger(channel.m_port, index.asIntBuffer());
|
||||
m_index = index.asIntBuffer().get(0);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_AnalogTrigger, channel.getChannel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the resources used by this object.
|
||||
*/
|
||||
public void free() {
|
||||
AnalogJNI.cleanAnalogTrigger(m_port);
|
||||
m_port = 0;
|
||||
if (m_ownsAnalog && m_analogInput != null) {
|
||||
m_analogInput.free();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upper and lower limits of the analog trigger. The limits are given in ADC codes. If
|
||||
* oversampling is used, the units must be scaled appropriately.
|
||||
*
|
||||
* @param lower the lower raw limit
|
||||
* @param upper the upper raw limit
|
||||
*/
|
||||
public void setLimitsRaw(final int lower, final int upper) {
|
||||
if (lower > upper) {
|
||||
throw new BoundaryException("Lower bound is greater than upper");
|
||||
}
|
||||
AnalogJNI.setAnalogTriggerLimitsRaw(m_port, lower, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upper and lower limits of the analog trigger. The limits are given as floating point
|
||||
* voltage values.
|
||||
*
|
||||
* @param lower the lower voltage limit
|
||||
* @param upper the upper voltage limit
|
||||
*/
|
||||
public void setLimitsVoltage(double lower, double upper) {
|
||||
if (lower > upper) {
|
||||
throw new BoundaryException("Lower bound is greater than upper bound");
|
||||
}
|
||||
AnalogJNI.setAnalogTriggerLimitsVoltage(m_port, lower, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the analog trigger to use the averaged vs. raw values. If the value is true, then the
|
||||
* averaged value is selected for the analog trigger, otherwise the immediate value is used.
|
||||
*
|
||||
* @param useAveragedValue true to use an averaged value, false otherwise
|
||||
*/
|
||||
public void setAveraged(boolean useAveragedValue) {
|
||||
AnalogJNI.setAnalogTriggerAveraged(m_port, useAveragedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the analog trigger to use a filtered value. The analog trigger will operate with a 3
|
||||
* point average rejection filter. This is designed to help with 360 degree pot applications for
|
||||
* the period where the pot crosses through zero.
|
||||
*
|
||||
* @param useFilteredValue true to use a filterd value, false otherwise
|
||||
*/
|
||||
public void setFiltered(boolean useFilteredValue) {
|
||||
AnalogJNI.setAnalogTriggerFiltered(m_port, useFilteredValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index of the analog trigger. This is the FPGA index of this analog trigger
|
||||
* instance.
|
||||
*
|
||||
* @return The index of the analog trigger.
|
||||
*/
|
||||
public int getIndex() {
|
||||
return m_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the InWindow output of the analog trigger. True if the analog input is between the upper
|
||||
* and lower limits.
|
||||
*
|
||||
* @return The InWindow output of the analog trigger.
|
||||
*/
|
||||
public boolean getInWindow() {
|
||||
return AnalogJNI.getAnalogTriggerInWindow(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TriggerState output of the analog trigger. True if above upper limit. False if below
|
||||
* lower limit. If in Hysteresis, maintain previous state.
|
||||
*
|
||||
* @return The TriggerState output of the analog trigger.
|
||||
*/
|
||||
public boolean getTriggerState() {
|
||||
return AnalogJNI.getAnalogTriggerTriggerState(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AnalogTriggerOutput object. Gets an output object that can be used for routing.
|
||||
* Caller is responsible for deleting the AnalogTriggerOutput object.
|
||||
*
|
||||
* @param type An enum of the type of output object to create.
|
||||
* @return A pointer to a new AnalogTriggerOutput object.
|
||||
*/
|
||||
public AnalogTriggerOutput createOutput(AnalogTriggerType type) {
|
||||
return new AnalogTriggerOutput(this, type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.AnalogJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Class to represent a specific output from an analog trigger. This class is used to get the
|
||||
* current output value and also as a DigitalSource to provide routing of an output to digital
|
||||
* subsystems on the FPGA such as Counter, Encoder, and Interrupt.
|
||||
*
|
||||
* <p>The TriggerState output indicates the primary output value of the trigger. If the analog
|
||||
* signal is less than the lower limit, the output is false. If the analog value is greater than the
|
||||
* upper limit, then the output is true. If the analog value is in between, then the trigger output
|
||||
* state maintains its most recent value.
|
||||
*
|
||||
* <p>The InWindow output indicates whether or not the analog signal is inside the range defined by
|
||||
* the limits.
|
||||
*
|
||||
* <p>The RisingPulse and FallingPulse outputs detect an instantaneous transition from above the
|
||||
* upper limit to below the lower limit, and vise versa. These pulses represent a rollover condition
|
||||
* of a sensor and can be routed to an up / down counter or to interrupts. Because the outputs
|
||||
* generate a pulse, they cannot be read directly. To help ensure that a rollover condition is not
|
||||
* missed, there is an average rejection filter available that operates on the upper 8 bits of a 12
|
||||
* bit number and selects the nearest outlyer of 3 samples. This will reject a sample that is (due
|
||||
* to averaging or sampling) errantly between the two limits. This filter will fail if more than one
|
||||
* sample in a row is errantly in between the two limits. You may see this problem if attempting to
|
||||
* use this feature with a mechanical rollover sensor, such as a 360 degree no-stop potentiometer
|
||||
* without signal conditioning, because the rollover transition is not sharp / clean enough. Using
|
||||
* the averaging engine may help with this, but rotational speeds of the sensor will then be
|
||||
* limited.
|
||||
*/
|
||||
public class AnalogTriggerOutput extends DigitalSource {
|
||||
|
||||
/**
|
||||
* Exceptions dealing with improper operation of the Analog trigger output.
|
||||
*/
|
||||
public class AnalogTriggerOutputException extends RuntimeException {
|
||||
/**
|
||||
* Create a new exception with the given message.
|
||||
*
|
||||
* @param message the message to pass with the exception
|
||||
*/
|
||||
public AnalogTriggerOutputException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private final AnalogTrigger m_trigger;
|
||||
private final AnalogTriggerType m_outputType;
|
||||
|
||||
/**
|
||||
* Create an object that represents one of the four outputs from an analog trigger.
|
||||
*
|
||||
* <p>Because this class derives from DigitalSource, it can be passed into routing functions for
|
||||
* Counter, Encoder, etc.
|
||||
*
|
||||
* @param trigger The trigger for which this is an output.
|
||||
* @param outputType An enum that specifies the output on the trigger to represent.
|
||||
*/
|
||||
public AnalogTriggerOutput(AnalogTrigger trigger, final AnalogTriggerType outputType) {
|
||||
requireNonNull(trigger, "Analog Trigger given was null");
|
||||
requireNonNull(outputType, "Analog Trigger Type given was null");
|
||||
|
||||
m_trigger = trigger;
|
||||
m_outputType = outputType;
|
||||
HAL.report(tResourceType.kResourceType_AnalogTriggerOutput,
|
||||
trigger.getIndex(), outputType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the resources for this output.
|
||||
*/
|
||||
public void free() {
|
||||
if (m_interrupt != 0) {
|
||||
cancelInterrupts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of the analog trigger output.
|
||||
*
|
||||
* @return The state of the analog trigger output.
|
||||
*/
|
||||
public boolean get() {
|
||||
return AnalogJNI.getAnalogTriggerOutput(m_trigger.m_port, m_outputType.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPortHandleForRouting() {
|
||||
return m_trigger.m_port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnalogTriggerTypeForRouting() {
|
||||
return m_outputType.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannel() {
|
||||
return m_trigger.m_index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnalogTrigger() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the state in which the AnalogTrigger triggers.
|
||||
*/
|
||||
public enum AnalogTriggerType {
|
||||
kInWindow(AnalogJNI.AnalogTriggerType.kInWindow), kState(AnalogJNI.AnalogTriggerType.kState),
|
||||
kRisingPulse(AnalogJNI.AnalogTriggerType.kRisingPulse),
|
||||
kFallingPulse(AnalogJNI.AnalogTriggerType.kFallingPulse);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
private final int value;
|
||||
|
||||
private AnalogTriggerType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014-2017. 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.hal.AccelerometerJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.interfaces.Accelerometer;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* Built-in accelerometer.
|
||||
*
|
||||
* <p>This class allows access to the roboRIO's internal accelerometer.
|
||||
*/
|
||||
public class BuiltInAccelerometer implements Accelerometer, LiveWindowSendable {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param range The range the accelerometer will measure
|
||||
*/
|
||||
public BuiltInAccelerometer(Range range) {
|
||||
setRange(range);
|
||||
HAL.report(tResourceType.kResourceType_Accelerometer, 0, 0, "Built-in accelerometer");
|
||||
LiveWindow.addSensor("BuiltInAccel", 0, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. The accelerometer will measure +/-8 g-forces
|
||||
*/
|
||||
public BuiltInAccelerometer() {
|
||||
this(Range.k8G);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(Range range) {
|
||||
AccelerometerJNI.setAccelerometerActive(false);
|
||||
|
||||
switch (range) {
|
||||
case k2G:
|
||||
AccelerometerJNI.setAccelerometerRange(0);
|
||||
break;
|
||||
case k4G:
|
||||
AccelerometerJNI.setAccelerometerRange(1);
|
||||
break;
|
||||
case k8G:
|
||||
AccelerometerJNI.setAccelerometerRange(2);
|
||||
break;
|
||||
case k16G:
|
||||
default:
|
||||
throw new IllegalArgumentException(range + " range not supported (use k2G, k4G, or k8G)");
|
||||
|
||||
}
|
||||
|
||||
AccelerometerJNI.setAccelerometerActive(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* The acceleration in the X axis.
|
||||
*
|
||||
* @return The acceleration of the roboRIO along the X axis in g-forces
|
||||
*/
|
||||
@Override
|
||||
public double getX() {
|
||||
return AccelerometerJNI.getAccelerometerX();
|
||||
}
|
||||
|
||||
/**
|
||||
* The acceleration in the Y axis.
|
||||
*
|
||||
* @return The acceleration of the roboRIO along the Y axis in g-forces
|
||||
*/
|
||||
@Override
|
||||
public double getY() {
|
||||
return AccelerometerJNI.getAccelerometerY();
|
||||
}
|
||||
|
||||
/**
|
||||
* The acceleration in the Z axis.
|
||||
*
|
||||
* @return The acceleration of the roboRIO along the Z axis in g-forces
|
||||
*/
|
||||
@Override
|
||||
public double getZ() {
|
||||
return AccelerometerJNI.getAccelerometerZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "3AxisAccelerometer";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("X", getX());
|
||||
m_table.putNumber("Y", getY());
|
||||
m_table.putNumber("Z", getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
public interface CANSpeedController extends SpeedController, PIDInterface, LiveWindowSendable {
|
||||
/**
|
||||
* Mode determines how the motor is controlled, used internally. This is meant to be subclassed by
|
||||
* enums
|
||||
*
|
||||
* <p>Note that the Jaguar does not support follower mode.
|
||||
*/
|
||||
interface ControlMode {
|
||||
/**
|
||||
* Gets the name of this control mode. Since this interface should only be subclassed by
|
||||
* enumerations, this will be overridden by the builtin name() method.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Checks if this control mode is PID-compatible.
|
||||
*/
|
||||
boolean isPID();
|
||||
|
||||
/**
|
||||
* Gets the integer value of this control mode.
|
||||
*/
|
||||
int getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current control mode.
|
||||
*
|
||||
* @return the current control mode
|
||||
*/
|
||||
ControlMode getControlMode();
|
||||
|
||||
/**
|
||||
* Sets the control mode of this speed controller.
|
||||
*
|
||||
* @param mode the the new mode
|
||||
*/
|
||||
void setControlMode(int mode);
|
||||
|
||||
/**
|
||||
* Set the proportional PID constant.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
void setP(double p);
|
||||
|
||||
/**
|
||||
* Set the integral PID constant.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
void setI(double i);
|
||||
|
||||
/**
|
||||
* Set the derivative PID constant.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
void setD(double d);
|
||||
|
||||
/**
|
||||
* Set the feed-forward PID constant. This method is optional to implement.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
default void setF(double f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the feed-forward PID constant. This method is optional to implement. If a subclass does
|
||||
* not implement this, it will always return zero.
|
||||
*/
|
||||
default double getF() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current input (battery) voltage.
|
||||
*
|
||||
* @return the input voltage to the controller (in Volts).
|
||||
*/
|
||||
double getBusVoltage();
|
||||
|
||||
/**
|
||||
* Get the current output voltage.
|
||||
*
|
||||
* @return the output voltage to the motor in volts.
|
||||
*/
|
||||
double getOutputVoltage();
|
||||
|
||||
/**
|
||||
* Get the current being applied to the motor.
|
||||
*
|
||||
* @return the current motor current (in Amperes).
|
||||
*/
|
||||
double getOutputCurrent();
|
||||
|
||||
/**
|
||||
* Get the current temperature of the controller.
|
||||
*
|
||||
* @return the current temperature of the controller, in degrees Celsius.
|
||||
*/
|
||||
double getTemperature();
|
||||
|
||||
/**
|
||||
* Return the current position of whatever the current selected sensor is.
|
||||
*
|
||||
* <p>See specific implementations for more information on selecting feedback sensors.
|
||||
*
|
||||
* @return the current sensor position.
|
||||
*/
|
||||
double getPosition();
|
||||
|
||||
/**
|
||||
* Return the current velocity of whatever the current selected sensor is.
|
||||
*
|
||||
* <p>See specific implementations for more information on selecting feedback sensors.
|
||||
*
|
||||
* @return the current sensor velocity.
|
||||
*/
|
||||
double getSpeed();
|
||||
|
||||
/**
|
||||
* Set the maximum rate of change of the output voltage.
|
||||
*
|
||||
* @param rampRate the maximum rate of change of the votlage, in Volts / sec.
|
||||
*/
|
||||
void setVoltageRampRate(double rampRate);
|
||||
|
||||
/**
|
||||
* All CAN Speed Controllers have the same SmartDashboard type: "CANSpeedController".
|
||||
*/
|
||||
String SMART_DASHBOARD_TYPE = "CANSpeedController";
|
||||
|
||||
@Override
|
||||
default void updateTable() {
|
||||
ITable table = getTable();
|
||||
if (table != null) {
|
||||
table.putString("~TYPE~", SMART_DASHBOARD_TYPE);
|
||||
table.putString("Type", getClass().getSimpleName());
|
||||
table.putNumber("Mode", getControlMode().getValue());
|
||||
if (getControlMode().isPID()) {
|
||||
table.putNumber("p", getP());
|
||||
table.putNumber("i", getI());
|
||||
table.putNumber("d", getD());
|
||||
table.putNumber("f", getF());
|
||||
}
|
||||
table.putBoolean("Enabled", isEnabled());
|
||||
table.putNumber("Value", get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default String getSmartDashboardType() {
|
||||
return SMART_DASHBOARD_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ITableListener for the LiveWindow table for this CAN speed controller.
|
||||
*/
|
||||
default ITableListener createTableListener() {
|
||||
return (table, key, value, isNew) -> {
|
||||
switch (key) {
|
||||
case "Enabled":
|
||||
if ((Boolean) value) {
|
||||
enable();
|
||||
} else {
|
||||
disable();
|
||||
}
|
||||
break;
|
||||
case "Value":
|
||||
set((Double) value);
|
||||
break;
|
||||
case "Mode":
|
||||
setControlMode(((Double) value).intValue());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (getControlMode().isPID()) {
|
||||
switch (key) {
|
||||
case "p":
|
||||
setP((Double) value);
|
||||
break;
|
||||
case "i":
|
||||
setI((Double) value);
|
||||
break;
|
||||
case "d":
|
||||
setD((Double) value);
|
||||
break;
|
||||
case "f":
|
||||
setF((Double) value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
756
wpilibj/src/main/java/edu/wpi/first/wpilibj/CameraServer.java
Normal file
756
wpilibj/src/main/java/edu/wpi/first/wpilibj/CameraServer.java
Normal file
@@ -0,0 +1,756 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.cscore.AxisCamera;
|
||||
import edu.wpi.cscore.CameraServerJNI;
|
||||
import edu.wpi.cscore.CvSink;
|
||||
import edu.wpi.cscore.CvSource;
|
||||
import edu.wpi.cscore.MjpegServer;
|
||||
import edu.wpi.cscore.UsbCamera;
|
||||
import edu.wpi.cscore.VideoEvent;
|
||||
import edu.wpi.cscore.VideoException;
|
||||
import edu.wpi.cscore.VideoListener;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.cscore.VideoMode.PixelFormat;
|
||||
import edu.wpi.cscore.VideoProperty;
|
||||
import edu.wpi.cscore.VideoSink;
|
||||
import edu.wpi.cscore.VideoSource;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTablesJNI;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* Singleton class for creating and keeping camera servers.
|
||||
* Also publishes camera information to NetworkTables.
|
||||
*/
|
||||
public class CameraServer {
|
||||
public static final int kBasePort = 1181;
|
||||
|
||||
@Deprecated
|
||||
public static final int kSize640x480 = 0;
|
||||
@Deprecated
|
||||
public static final int kSize320x240 = 1;
|
||||
@Deprecated
|
||||
public static final int kSize160x120 = 2;
|
||||
|
||||
private static final String kPublishName = "/CameraPublisher";
|
||||
private static CameraServer server;
|
||||
|
||||
/**
|
||||
* Get the CameraServer instance.
|
||||
*/
|
||||
public static synchronized CameraServer getInstance() {
|
||||
if (server == null) {
|
||||
server = new CameraServer();
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
private AtomicInteger m_defaultUsbDevice;
|
||||
private String m_primarySourceName;
|
||||
private final Hashtable<String, VideoSource> m_sources;
|
||||
private final Hashtable<String, VideoSink> m_sinks;
|
||||
private final Hashtable<Integer, ITable> m_tables; // indexed by source handle
|
||||
private final ITable m_publishTable;
|
||||
private final VideoListener m_videoListener; //NOPMD
|
||||
private final int m_tableListener; //NOPMD
|
||||
private int m_nextPort;
|
||||
private String[] m_addresses;
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String makeSourceValue(int source) {
|
||||
switch (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))) {
|
||||
case kUsb:
|
||||
return "usb:" + CameraServerJNI.getUsbCameraPath(source);
|
||||
case kHttp: {
|
||||
String[] urls = CameraServerJNI.getHttpCameraUrls(source);
|
||||
if (urls.length > 0) {
|
||||
return "ip:" + urls[0];
|
||||
} else {
|
||||
return "ip:";
|
||||
}
|
||||
}
|
||||
case kCv:
|
||||
// FIXME: Should be "cv:", but LabVIEW dashboard requires "usb:".
|
||||
// https://github.com/wpilibsuite/allwpilib/issues/407
|
||||
return "usb:";
|
||||
default:
|
||||
return "unknown:";
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String makeStreamValue(String address, int port) {
|
||||
return "mjpg:http://" + address + ":" + port + "/?action=stream";
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
||||
private synchronized String[] getSinkStreamValues(int sink) {
|
||||
// Ignore all but MjpegServer
|
||||
if (VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink)) != VideoSink.Kind.kMjpeg) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
// Get port
|
||||
int port = CameraServerJNI.getMjpegServerPort(sink);
|
||||
|
||||
// Generate values
|
||||
ArrayList<String> values = new ArrayList<>(m_addresses.length + 1);
|
||||
String listenAddress = CameraServerJNI.getMjpegServerListenAddress(sink);
|
||||
if (!listenAddress.isEmpty()) {
|
||||
// If a listen address is specified, only use that
|
||||
values.add(makeStreamValue(listenAddress, port));
|
||||
} else {
|
||||
// Otherwise generate for hostname and all interface addresses
|
||||
values.add(makeStreamValue(CameraServerJNI.getHostname() + ".local", port));
|
||||
for (String addr : m_addresses) {
|
||||
if (addr.equals("127.0.0.1")) {
|
||||
continue; // ignore localhost
|
||||
}
|
||||
values.add(makeStreamValue(addr, port));
|
||||
}
|
||||
}
|
||||
|
||||
return values.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
||||
private synchronized String[] getSourceStreamValues(int source) {
|
||||
// Ignore all but HttpCamera
|
||||
if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))
|
||||
!= VideoSource.Kind.kHttp) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
// Generate values
|
||||
String[] values = CameraServerJNI.getHttpCameraUrls(source);
|
||||
for (int j = 0; j < values.length; j++) {
|
||||
values[j] = "mjpg:" + values[j];
|
||||
}
|
||||
|
||||
// Look to see if we have a passthrough server for this source
|
||||
for (VideoSink i : m_sinks.values()) {
|
||||
int sink = i.getHandle();
|
||||
int sinkSource = CameraServerJNI.getSinkSource(sink);
|
||||
if (source == sinkSource
|
||||
&& VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink)) == VideoSink.Kind.kMjpeg) {
|
||||
// Add USB-only passthrough
|
||||
String[] finalValues = new String[values.length + 1];
|
||||
for (int j = 0; j < values.length; j++) {
|
||||
finalValues[j] = values[j];
|
||||
}
|
||||
int port = CameraServerJNI.getMjpegServerPort(sink);
|
||||
finalValues[values.length] = makeStreamValue("172.22.11.2", port);
|
||||
return finalValues;
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
||||
private synchronized void updateStreamValues() {
|
||||
// Over all the sinks...
|
||||
for (VideoSink i : m_sinks.values()) {
|
||||
int sink = i.getHandle();
|
||||
|
||||
// Get the source's subtable (if none exists, we're done)
|
||||
int source = CameraServerJNI.getSinkSource(sink);
|
||||
if (source == 0) {
|
||||
continue;
|
||||
}
|
||||
ITable table = m_tables.get(source);
|
||||
if (table != null) {
|
||||
// Don't set stream values if this is a HttpCamera passthrough
|
||||
if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))
|
||||
== VideoSource.Kind.kHttp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set table value
|
||||
String[] values = getSinkStreamValues(sink);
|
||||
if (values.length > 0) {
|
||||
table.putStringArray("streams", values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Over all the sources...
|
||||
for (VideoSource i : m_sources.values()) {
|
||||
int source = i.getHandle();
|
||||
|
||||
// Get the source's subtable (if none exists, we're done)
|
||||
ITable table = m_tables.get(source);
|
||||
if (table != null) {
|
||||
// Set table value
|
||||
String[] values = getSourceStreamValues(source);
|
||||
if (values.length > 0) {
|
||||
table.putStringArray("streams", values);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String pixelFormatToString(PixelFormat pixelFormat) {
|
||||
switch (pixelFormat) {
|
||||
case kMJPEG:
|
||||
return "MJPEG";
|
||||
case kYUYV:
|
||||
return "YUYV";
|
||||
case kRGB565:
|
||||
return "RGB565";
|
||||
case kBGR:
|
||||
return "BGR";
|
||||
case kGray:
|
||||
return "Gray";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide string description of video mode.
|
||||
/// The returned string is "{width}x{height} {format} {fps} fps".
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String videoModeToString(VideoMode mode) {
|
||||
return mode.width + "x" + mode.height + " " + pixelFormatToString(mode.pixelFormat)
|
||||
+ " " + mode.fps + " fps";
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String[] getSourceModeValues(int sourceHandle) {
|
||||
VideoMode[] modes = CameraServerJNI.enumerateSourceVideoModes(sourceHandle);
|
||||
String[] modeStrings = new String[modes.length];
|
||||
for (int i = 0; i < modes.length; i++) {
|
||||
modeStrings[i] = videoModeToString(modes[i]);
|
||||
}
|
||||
return modeStrings;
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static void putSourcePropertyValue(ITable table, VideoEvent event, boolean isNew) {
|
||||
String name;
|
||||
String infoName;
|
||||
if (event.name.startsWith("raw_")) {
|
||||
name = "RawProperty/" + event.name;
|
||||
infoName = "RawPropertyInfo/" + event.name;
|
||||
} else {
|
||||
name = "Property/" + event.name;
|
||||
infoName = "PropertyInfo/" + event.name;
|
||||
}
|
||||
|
||||
switch (event.propertyKind) {
|
||||
case kBoolean:
|
||||
if (isNew) {
|
||||
table.setDefaultBoolean(name, event.value != 0);
|
||||
} else {
|
||||
table.putBoolean(name, event.value != 0);
|
||||
}
|
||||
break;
|
||||
case kInteger:
|
||||
case kEnum:
|
||||
if (isNew) {
|
||||
table.setDefaultNumber(name, event.value);
|
||||
table.putNumber(infoName + "/min",
|
||||
CameraServerJNI.getPropertyMin(event.propertyHandle));
|
||||
table.putNumber(infoName + "/max",
|
||||
CameraServerJNI.getPropertyMax(event.propertyHandle));
|
||||
table.putNumber(infoName + "/step",
|
||||
CameraServerJNI.getPropertyStep(event.propertyHandle));
|
||||
table.putNumber(infoName + "/default",
|
||||
CameraServerJNI.getPropertyDefault(event.propertyHandle));
|
||||
} else {
|
||||
table.putNumber(name, event.value);
|
||||
}
|
||||
break;
|
||||
case kString:
|
||||
if (isNew) {
|
||||
table.setDefaultString(name, event.valueStr);
|
||||
} else {
|
||||
table.putString(name, event.valueStr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.UnusedLocalVariable"})
|
||||
private CameraServer() {
|
||||
m_defaultUsbDevice = new AtomicInteger();
|
||||
m_sources = new Hashtable<>();
|
||||
m_sinks = new Hashtable<>();
|
||||
m_tables = new Hashtable<>();
|
||||
m_publishTable = NetworkTable.getTable(kPublishName);
|
||||
m_nextPort = kBasePort;
|
||||
m_addresses = new String[0];
|
||||
|
||||
// We publish sources to NetworkTables using the following structure:
|
||||
// "/CameraPublisher/{Source.Name}/" - root
|
||||
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
|
||||
// - "streams" (string array): URLs that can be used to stream data
|
||||
// - "description" (string): Description of the source
|
||||
// - "connected" (boolean): Whether source is connected
|
||||
// - "mode" (string): Current video mode
|
||||
// - "modes" (string array): Available video modes
|
||||
// - "Property/{Property}" - Property values
|
||||
// - "PropertyInfo/{Property}" - Property supporting information
|
||||
|
||||
// Listener for video events
|
||||
m_videoListener = new VideoListener(event -> {
|
||||
switch (event.kind) {
|
||||
case kSourceCreated: {
|
||||
// Create subtable for the camera
|
||||
ITable table = m_publishTable.getSubTable(event.name);
|
||||
m_tables.put(event.sourceHandle, table);
|
||||
table.putString("source", makeSourceValue(event.sourceHandle));
|
||||
table.putString("description",
|
||||
CameraServerJNI.getSourceDescription(event.sourceHandle));
|
||||
table.putBoolean("connected", CameraServerJNI.isSourceConnected(event.sourceHandle));
|
||||
table.putStringArray("streams", getSourceStreamValues(event.sourceHandle));
|
||||
try {
|
||||
VideoMode mode = CameraServerJNI.getSourceVideoMode(event.sourceHandle);
|
||||
table.setDefaultString("mode", videoModeToString(mode));
|
||||
table.putStringArray("modes", getSourceModeValues(event.sourceHandle));
|
||||
} catch (VideoException ex) {
|
||||
// Do nothing. Let the other event handlers update this if there is an error.
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceDestroyed: {
|
||||
ITable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
table.putString("source", "");
|
||||
table.putStringArray("streams", new String[0]);
|
||||
table.putStringArray("modes", new String[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceConnected: {
|
||||
ITable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
// update the description too (as it may have changed)
|
||||
table.putString("description",
|
||||
CameraServerJNI.getSourceDescription(event.sourceHandle));
|
||||
table.putBoolean("connected", true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceDisconnected: {
|
||||
ITable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
table.putBoolean("connected", false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceVideoModesUpdated: {
|
||||
ITable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
table.putStringArray("modes", getSourceModeValues(event.sourceHandle));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceVideoModeChanged: {
|
||||
ITable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
table.putString("mode", videoModeToString(event.mode));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyCreated: {
|
||||
ITable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
putSourcePropertyValue(table, event, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyValueUpdated: {
|
||||
ITable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
putSourcePropertyValue(table, event, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyChoicesUpdated: {
|
||||
ITable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
String[] choices = CameraServerJNI.getEnumPropertyChoices(event.propertyHandle);
|
||||
table.putStringArray("PropertyInfo/" + event.name + "/choices", choices);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSinkSourceChanged:
|
||||
case kSinkCreated:
|
||||
case kSinkDestroyed:
|
||||
case kNetworkInterfacesChanged: {
|
||||
m_addresses = CameraServerJNI.getNetworkInterfaces();
|
||||
updateStreamValues();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, 0x4fff, true);
|
||||
|
||||
// Listener for NetworkTable events
|
||||
// We don't currently support changing settings via NT due to
|
||||
// synchronization issues, so just update to current setting if someone
|
||||
// else tries to change it.
|
||||
m_tableListener = NetworkTablesJNI.addEntryListener(kPublishName + "/",
|
||||
(uid, key, eventValue, flags) -> {
|
||||
String relativeKey = key.substring(kPublishName.length() + 1);
|
||||
|
||||
// get source (sourceName/...)
|
||||
int subKeyIndex = relativeKey.indexOf('/');
|
||||
if (subKeyIndex == -1) {
|
||||
return;
|
||||
}
|
||||
String sourceName = relativeKey.substring(0, subKeyIndex);
|
||||
VideoSource source = m_sources.get(sourceName);
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get subkey
|
||||
relativeKey = relativeKey.substring(subKeyIndex + 1);
|
||||
|
||||
// handle standard names
|
||||
String propName;
|
||||
if (relativeKey.equals("mode")) {
|
||||
// reset to current mode
|
||||
NetworkTablesJNI.putString(key, videoModeToString(source.getVideoMode()));
|
||||
return;
|
||||
} else if (relativeKey.startsWith("Property/")) {
|
||||
propName = relativeKey.substring(9);
|
||||
} else if (relativeKey.startsWith("RawProperty/")) {
|
||||
propName = relativeKey.substring(12);
|
||||
} else {
|
||||
return; // ignore
|
||||
}
|
||||
|
||||
// everything else is a property
|
||||
VideoProperty property = source.getProperty(propName);
|
||||
switch (property.getKind()) {
|
||||
case kNone:
|
||||
return;
|
||||
case kBoolean:
|
||||
// reset to current setting
|
||||
NetworkTablesJNI.putBoolean(key, property.get() != 0);
|
||||
return;
|
||||
case kInteger:
|
||||
case kEnum:
|
||||
// reset to current setting
|
||||
NetworkTablesJNI.putDouble(key, property.get());
|
||||
return;
|
||||
case kString:
|
||||
// reset to current setting
|
||||
NetworkTablesJNI.putString(key, property.getString());
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}, ITable.NOTIFY_IMMEDIATE | ITable.NOTIFY_UPDATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* <p>You should call this method to see a camera feed on the dashboard.
|
||||
* If you also want to perform vision processing on the roboRIO, use
|
||||
* getVideo() to get access to the camera images.
|
||||
*
|
||||
* <p>The first time this overload is called, it calls
|
||||
* {@link #startAutomaticCapture(int)} with device 0, creating a camera
|
||||
* named "USB Camera 0". Subsequent calls increment the device number
|
||||
* (e.g. 1, 2, etc).
|
||||
*/
|
||||
public UsbCamera startAutomaticCapture() {
|
||||
return startAutomaticCapture(m_defaultUsbDevice.getAndIncrement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* <p>This overload calls {@link #startAutomaticCapture(String, int)} with
|
||||
* a name of "USB Camera {dev}".
|
||||
*
|
||||
* @param dev The device number of the camera interface
|
||||
*/
|
||||
public UsbCamera startAutomaticCapture(int dev) {
|
||||
UsbCamera camera = new UsbCamera("USB Camera " + dev, dev);
|
||||
startAutomaticCapture(camera);
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param dev The device number of the camera interface
|
||||
*/
|
||||
public UsbCamera startAutomaticCapture(String name, int dev) {
|
||||
UsbCamera camera = new UsbCamera(name, dev);
|
||||
startAutomaticCapture(camera);
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param path The device path (e.g. "/dev/video0") of the camera
|
||||
*/
|
||||
public UsbCamera startAutomaticCapture(String name, String path) {
|
||||
UsbCamera camera = new UsbCamera(name, path);
|
||||
startAutomaticCapture(camera);
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard from
|
||||
* an existing camera.
|
||||
*
|
||||
* @param camera Camera
|
||||
*/
|
||||
public void startAutomaticCapture(VideoSource camera) {
|
||||
addCamera(camera);
|
||||
VideoSink server = addServer("serve_" + camera.getName());
|
||||
server.setSource(camera);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* <p>This overload calls {@link #addAxisCamera(String, String)} with
|
||||
* name "Axis Camera".
|
||||
*
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
public AxisCamera addAxisCamera(String host) {
|
||||
return addAxisCamera("Axis Camera", host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* <p>This overload calls {@link #addAxisCamera(String, String[])} with
|
||||
* name "Axis Camera".
|
||||
*
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
public AxisCamera addAxisCamera(String[] hosts) {
|
||||
return addAxisCamera("Axis Camera", hosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
public AxisCamera addAxisCamera(String name, String host) {
|
||||
AxisCamera camera = new AxisCamera(name, host);
|
||||
// Create a passthrough MJPEG server for USB access
|
||||
startAutomaticCapture(camera);
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
public AxisCamera addAxisCamera(String name, String[] hosts) {
|
||||
AxisCamera camera = new AxisCamera(name, hosts);
|
||||
// Create a passthrough MJPEG server for USB access
|
||||
startAutomaticCapture(camera);
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the primary camera feed. This allows you to
|
||||
* get images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* <p>This is only valid to call after a camera feed has been added
|
||||
* with startAutomaticCapture() or addServer().
|
||||
*/
|
||||
public CvSink getVideo() {
|
||||
VideoSource source;
|
||||
synchronized (this) {
|
||||
if (m_primarySourceName == null) {
|
||||
throw new VideoException("no camera available");
|
||||
}
|
||||
source = m_sources.get(m_primarySourceName);
|
||||
}
|
||||
if (source == null) {
|
||||
throw new VideoException("no camera available");
|
||||
}
|
||||
return getVideo(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the specified camera. This allows you to get
|
||||
* images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* @param camera Camera (e.g. as returned by startAutomaticCapture).
|
||||
*/
|
||||
public CvSink getVideo(VideoSource camera) {
|
||||
String name = "opencv_" + camera.getName();
|
||||
|
||||
synchronized (this) {
|
||||
VideoSink sink = m_sinks.get(name);
|
||||
if (sink != null) {
|
||||
VideoSink.Kind kind = sink.getKind();
|
||||
if (kind != VideoSink.Kind.kCv) {
|
||||
throw new VideoException("expected OpenCV sink, but got " + kind);
|
||||
}
|
||||
return (CvSink) sink;
|
||||
}
|
||||
}
|
||||
|
||||
CvSink newsink = new CvSink(name);
|
||||
newsink.setSource(camera);
|
||||
addServer(newsink);
|
||||
return newsink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the specified camera. This allows you to get
|
||||
* images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* @param name Camera name
|
||||
*/
|
||||
public CvSink getVideo(String name) {
|
||||
VideoSource source;
|
||||
synchronized (this) {
|
||||
source = m_sources.get(name);
|
||||
if (source == null) {
|
||||
throw new VideoException("could not find camera " + name);
|
||||
}
|
||||
}
|
||||
return getVideo(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MJPEG stream with OpenCV input. This can be called to pass custom
|
||||
* annotated images to the dashboard.
|
||||
*
|
||||
* @param name Name to give the stream
|
||||
* @param width Width of the image being sent
|
||||
* @param height Height of the image being sent
|
||||
*/
|
||||
public CvSource putVideo(String name, int width, int height) {
|
||||
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, width, height, 30);
|
||||
startAutomaticCapture(source);
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a MJPEG server at the next available port.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
public MjpegServer addServer(String name) {
|
||||
int port;
|
||||
synchronized (this) {
|
||||
port = m_nextPort;
|
||||
m_nextPort++;
|
||||
}
|
||||
return addServer(name, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a MJPEG server.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
public MjpegServer addServer(String name, int port) {
|
||||
MjpegServer server = new MjpegServer(name, port);
|
||||
addServer(server);
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an already created server.
|
||||
*
|
||||
* @param server Server
|
||||
*/
|
||||
public void addServer(VideoSink server) {
|
||||
synchronized (this) {
|
||||
m_sinks.put(server.getName(), server);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a server by name.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
public void removeServer(String name) {
|
||||
synchronized (this) {
|
||||
m_sinks.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server for the primary camera feed.
|
||||
*
|
||||
* <p>This is only valid to call after a camera feed has been added
|
||||
* with startAutomaticCapture() or addServer().
|
||||
*/
|
||||
public VideoSink getServer() {
|
||||
synchronized (this) {
|
||||
if (m_primarySourceName == null) {
|
||||
throw new VideoException("no camera available");
|
||||
}
|
||||
return getServer("serve_" + m_primarySourceName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a server by name.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
public VideoSink getServer(String name) {
|
||||
synchronized (this) {
|
||||
return m_sinks.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an already created camera.
|
||||
*
|
||||
* @param camera Camera
|
||||
*/
|
||||
public void addCamera(VideoSource camera) {
|
||||
String name = camera.getName();
|
||||
synchronized (this) {
|
||||
if (m_primarySourceName == null) {
|
||||
m_primarySourceName = name;
|
||||
}
|
||||
m_sources.put(name, camera);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a camera by name.
|
||||
*
|
||||
* @param name Camera name
|
||||
*/
|
||||
public void removeCamera(String name) {
|
||||
synchronized (this) {
|
||||
m_sources.remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
154
wpilibj/src/main/java/edu/wpi/first/wpilibj/CircularBuffer.java
Normal file
154
wpilibj/src/main/java/edu/wpi/first/wpilibj/CircularBuffer.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2017. 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;
|
||||
|
||||
/**
|
||||
* This is a simple circular buffer so we don't need to "bucket brigade" copy old values.
|
||||
*/
|
||||
public class CircularBuffer {
|
||||
private double[] m_data;
|
||||
|
||||
// Index of element at front of buffer
|
||||
private int m_front = 0;
|
||||
|
||||
// Number of elements used in buffer
|
||||
private int m_length = 0;
|
||||
|
||||
/**
|
||||
* Create a CircularBuffer with the provided size.
|
||||
*
|
||||
* @param size The size of the circular buffer.
|
||||
*/
|
||||
public CircularBuffer(int size) {
|
||||
m_data = new double[size];
|
||||
for (int i = 0; i < m_data.length; i++) {
|
||||
m_data[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push new value onto front of the buffer. The value at the back is overwritten if the buffer is
|
||||
* full.
|
||||
*/
|
||||
public void pushFront(double value) {
|
||||
if (m_data.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_front = moduloDec(m_front);
|
||||
|
||||
m_data[m_front] = value;
|
||||
|
||||
if (m_length < m_data.length) {
|
||||
m_length++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push new value onto back of the buffer. The value at the front is overwritten if the buffer is
|
||||
* full.
|
||||
*/
|
||||
public void pushBack(double value) {
|
||||
if (m_data.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_data[(m_front + m_length) % m_data.length] = value;
|
||||
|
||||
if (m_length < m_data.length) {
|
||||
m_length++;
|
||||
} else {
|
||||
// Increment front if buffer is full to maintain size
|
||||
m_front = moduloInc(m_front);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop value at front of buffer.
|
||||
*
|
||||
* @return value at front of buffer
|
||||
*/
|
||||
public double popFront() {
|
||||
// If there are no elements in the buffer, do nothing
|
||||
if (m_length == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double temp = m_data[m_front];
|
||||
m_front = moduloInc(m_front);
|
||||
m_length--;
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pop value at back of buffer.
|
||||
*/
|
||||
public double popBack() {
|
||||
// If there are no elements in the buffer, do nothing
|
||||
if (m_length == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
m_length--;
|
||||
return m_data[(m_front + m_length) % m_data.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes internal buffer to given size.
|
||||
*
|
||||
* <p>A new buffer is allocated because arrays are not resizable.
|
||||
*/
|
||||
void resize(int size) {
|
||||
double[] newBuffer = new double[size];
|
||||
m_length = Math.min(m_length, size);
|
||||
for (int i = 0; i < m_length; i++) {
|
||||
newBuffer[i] = m_data[(m_front + i) % m_data.length];
|
||||
}
|
||||
m_data = newBuffer;
|
||||
m_front = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets internal buffer contents to zero.
|
||||
*/
|
||||
public void reset() {
|
||||
for (int i = 0; i < m_data.length; i++) {
|
||||
m_data[i] = 0.0;
|
||||
}
|
||||
m_front = 0;
|
||||
m_length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element at the provided index relative to the start of the buffer.
|
||||
*
|
||||
* @return Element at index starting from front of buffer.
|
||||
*/
|
||||
public double get(int index) {
|
||||
return m_data[(m_front + index) % m_data.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment an index modulo the length of the m_data buffer.
|
||||
*/
|
||||
private int moduloInc(int index) {
|
||||
return (index + 1) % m_data.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement an index modulo the length of the m_data buffer.
|
||||
*/
|
||||
private int moduloDec(int index) {
|
||||
if (index == 0) {
|
||||
return m_data.length - 1;
|
||||
} else {
|
||||
return index - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
223
wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java
Normal file
223
wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java
Normal file
@@ -0,0 +1,223 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.hal.CompressorJNI;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* Class for operating a compressor connected to a PCM (Pneumatic Control Module). The PCM will
|
||||
* automatically run in closed loop mode by default whenever a {@link Solenoid} object is created.
|
||||
* For most cases, a Compressor object does not need to be instantiated or used in a robot program.
|
||||
* This class is only required in cases where the robot program needs a more detailed status of the
|
||||
* compressor or to enable/disable closed loop control.
|
||||
*
|
||||
* <p>Note: you cannot operate the compressor directly from this class as doing so would circumvent
|
||||
* the safety provided by using the pressure switch and closed loop control. You can only turn off
|
||||
* closed loop control, thereby stopping the compressor from operating.
|
||||
*/
|
||||
public class Compressor extends SensorBase implements LiveWindowSendable {
|
||||
private int m_compressorHandle;
|
||||
private byte m_module;
|
||||
|
||||
/**
|
||||
* Makes a new instance of the compressor using the provided CAN device ID. Use this constructor
|
||||
* when you have more than one PCM.
|
||||
*
|
||||
* @param module The PCM CAN device ID (0 - 62 inclusive)
|
||||
*/
|
||||
public Compressor(int module) {
|
||||
m_table = null;
|
||||
m_module = (byte) module;
|
||||
|
||||
m_compressorHandle = CompressorJNI.initializeCompressor((byte) module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a new instance of the compressor using the default PCM ID of 0.
|
||||
*
|
||||
* <p>Additional modules can be supported by making a new instance and {@link #Compressor(int)
|
||||
* specifying the CAN ID.}
|
||||
*/
|
||||
public Compressor() {
|
||||
this(getDefaultSolenoidModule());
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the compressor running in closed loop control mode.
|
||||
*
|
||||
* <p>Use the method in cases where you would like to manually stop and start the compressor for
|
||||
* applications such as conserving battery or making sure that the compressor motor doesn't start
|
||||
* during critical operations.
|
||||
*/
|
||||
public void start() {
|
||||
setClosedLoopControl(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the compressor from running in closed loop control mode.
|
||||
*
|
||||
* <p>Use the method in cases where you would like to manually stop and start the compressor for
|
||||
* applications such as conserving battery or making sure that the compressor motor doesn't start
|
||||
* during critical operations.
|
||||
*/
|
||||
public void stop() {
|
||||
setClosedLoopControl(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of the compressor.
|
||||
*
|
||||
* @return true if the compressor is on
|
||||
*/
|
||||
public boolean enabled() {
|
||||
return CompressorJNI.getCompressor(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pressure switch value.
|
||||
*
|
||||
* @return true if the pressure is low
|
||||
*/
|
||||
public boolean getPressureSwitchValue() {
|
||||
return CompressorJNI.getCompressorPressureSwitch(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current being used by the compressor.
|
||||
*
|
||||
* @return current consumed by the compressor in amps
|
||||
*/
|
||||
public double getCompressorCurrent() {
|
||||
return CompressorJNI.getCompressorCurrent(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PCM in closed loop control mode.
|
||||
*
|
||||
* @param on if true sets the compressor to be in closed loop control mode (default)
|
||||
*/
|
||||
public void setClosedLoopControl(boolean on) {
|
||||
CompressorJNI.setCompressorClosedLoopControl(m_compressorHandle, on);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current operating mode of the PCM.
|
||||
*
|
||||
* @return true if compressor is operating on closed-loop mode
|
||||
*/
|
||||
public boolean getClosedLoopControl() {
|
||||
return CompressorJNI.getCompressorClosedLoopControl(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* If PCM is in fault state : Compressor Drive is disabled due to compressor current being too
|
||||
* high.
|
||||
*
|
||||
* @return true if PCM is in fault state.
|
||||
*/
|
||||
public boolean getCompressorCurrentTooHighFault() {
|
||||
return CompressorJNI.getCompressorCurrentTooHighFault(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* If PCM sticky fault is set : Compressor is disabled due to compressor current being too
|
||||
* high.
|
||||
*
|
||||
* @return true if PCM sticky fault is set.
|
||||
*/
|
||||
public boolean getCompressorCurrentTooHighStickyFault() {
|
||||
return CompressorJNI.getCompressorCurrentTooHighStickyFault(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* If PCM sticky fault is set : Compressor output appears to be shorted.
|
||||
*
|
||||
* @return true if PCM sticky fault is set.
|
||||
*/
|
||||
public boolean getCompressorShortedStickyFault() {
|
||||
return CompressorJNI.getCompressorShortedStickyFault(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* If PCM is in fault state : Compressor output appears to be shorted.
|
||||
*
|
||||
* @return true if PCM is in fault state.
|
||||
*/
|
||||
public boolean getCompressorShortedFault() {
|
||||
return CompressorJNI.getCompressorShortedFault(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* If PCM sticky fault is set : Compressor does not appear to be wired, i.e. compressor is not
|
||||
* drawing enough current.
|
||||
*
|
||||
* @return true if PCM sticky fault is set.
|
||||
*/
|
||||
public boolean getCompressorNotConnectedStickyFault() {
|
||||
return CompressorJNI.getCompressorNotConnectedStickyFault(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* If PCM is in fault state : Compressor does not appear to be wired, i.e. compressor is not
|
||||
* drawing enough current.
|
||||
*
|
||||
* @return true if PCM is in fault state.
|
||||
*/
|
||||
public boolean getCompressorNotConnectedFault() {
|
||||
return CompressorJNI.getCompressorNotConnectedFault(m_compressorHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear ALL sticky faults inside PCM that Compressor is wired to.
|
||||
*
|
||||
* <p>If a sticky fault is set, then it will be persistently cleared. The compressor might
|
||||
* momentarily disable while the flags are being cleared. Doo not call this method too
|
||||
* frequently, otherwise normal compressor functionality may be prevented.
|
||||
*
|
||||
* <p>If no sticky faults are set then this call will have no effect.
|
||||
*/
|
||||
public void clearAllPCMStickyFaults() {
|
||||
CompressorJNI.clearAllPCMStickyFaults(m_module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Compressor";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putBoolean("Enabled", enabled());
|
||||
m_table.putBoolean("Pressure Switch", getPressureSwitchValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
25
wpilibj/src/main/java/edu/wpi/first/wpilibj/Controller.java
Normal file
25
wpilibj/src/main/java/edu/wpi/first/wpilibj/Controller.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* An interface for controllers. Controllers run control loops, the most command are PID controllers
|
||||
* and there variants, but this includes anything that is controlling an actuator in a separate
|
||||
* thread.
|
||||
*/
|
||||
interface Controller {
|
||||
/**
|
||||
* Allows the control loop to run.
|
||||
*/
|
||||
void enable();
|
||||
|
||||
/**
|
||||
* Stops the control loop from running until explicitly re-enabled by calling {@link #enable()}.
|
||||
*/
|
||||
void disable();
|
||||
}
|
||||
141
wpilibj/src/main/java/edu/wpi/first/wpilibj/ControllerPower.java
Normal file
141
wpilibj/src/main/java/edu/wpi/first/wpilibj/ControllerPower.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.PowerJNI;
|
||||
|
||||
public class ControllerPower {
|
||||
/**
|
||||
* Get the input voltage to the robot controller.
|
||||
*
|
||||
* @return The controller input voltage value in Volts
|
||||
*/
|
||||
public static double getInputVoltage() {
|
||||
return PowerJNI.getVinVoltage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input current to the robot controller.
|
||||
*
|
||||
* @return The controller input current value in Amps
|
||||
*/
|
||||
public static double getInputCurrent() {
|
||||
return PowerJNI.getVinCurrent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the voltage of the 3.3V rail.
|
||||
*
|
||||
* @return The controller 3.3V rail voltage value in Volts
|
||||
*/
|
||||
public static double getVoltage3V3() {
|
||||
return PowerJNI.getUserVoltage3V3();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current output of the 3.3V rail.
|
||||
*
|
||||
* @return The controller 3.3V rail output current value in Volts
|
||||
*/
|
||||
public static double getCurrent3V3() {
|
||||
return PowerJNI.getUserCurrent3V3();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled state of the 3.3V rail. The rail may be disabled due to a controller brownout,
|
||||
* a short circuit on the rail, or controller over-voltage.
|
||||
*
|
||||
* @return The controller 3.3V rail enabled value
|
||||
*/
|
||||
public static boolean getEnabled3V3() {
|
||||
return PowerJNI.getUserActive3V3();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of the total current faults on the 3.3V rail since the controller has booted.
|
||||
*
|
||||
* @return The number of faults
|
||||
*/
|
||||
public static int getFaultCount3V3() {
|
||||
return PowerJNI.getUserCurrentFaults3V3();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the voltage of the 5V rail.
|
||||
*
|
||||
* @return The controller 5V rail voltage value in Volts
|
||||
*/
|
||||
public static double getVoltage5V() {
|
||||
return PowerJNI.getUserVoltage5V();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current output of the 5V rail.
|
||||
*
|
||||
* @return The controller 5V rail output current value in Amps
|
||||
*/
|
||||
public static double getCurrent5V() {
|
||||
return PowerJNI.getUserCurrent5V();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled state of the 5V rail. The rail may be disabled due to a controller brownout, a
|
||||
* short circuit on the rail, or controller over-voltage.
|
||||
*
|
||||
* @return The controller 5V rail enabled value
|
||||
*/
|
||||
public static boolean getEnabled5V() {
|
||||
return PowerJNI.getUserActive5V();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of the total current faults on the 5V rail since the controller has booted.
|
||||
*
|
||||
* @return The number of faults
|
||||
*/
|
||||
public static int getFaultCount5V() {
|
||||
return PowerJNI.getUserCurrentFaults5V();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the voltage of the 6V rail.
|
||||
*
|
||||
* @return The controller 6V rail voltage value in Volts
|
||||
*/
|
||||
public static double getVoltage6V() {
|
||||
return PowerJNI.getUserVoltage6V();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current output of the 6V rail.
|
||||
*
|
||||
* @return The controller 6V rail output current value in Amps
|
||||
*/
|
||||
public static double getCurrent6V() {
|
||||
return PowerJNI.getUserCurrent6V();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled state of the 6V rail. The rail may be disabled due to a controller brownout, a
|
||||
* short circuit on the rail, or controller over-voltage.
|
||||
*
|
||||
* @return The controller 6V rail enabled value
|
||||
*/
|
||||
public static boolean getEnabled6V() {
|
||||
return PowerJNI.getUserActive6V();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of the total current faults on the 6V rail since the controller has booted.
|
||||
*
|
||||
* @return The number of faults
|
||||
*/
|
||||
public static int getFaultCount6V() {
|
||||
return PowerJNI.getUserCurrentFaults6V();
|
||||
}
|
||||
}
|
||||
586
wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java
Normal file
586
wpilibj/src/main/java/edu/wpi/first/wpilibj/Counter.java
Normal file
@@ -0,0 +1,586 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import edu.wpi.first.wpilibj.AnalogTriggerOutput.AnalogTriggerType;
|
||||
import edu.wpi.first.wpilibj.hal.CounterJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Class for counting the number of ticks on a digital input channel.
|
||||
*
|
||||
* <p>This is a general purpose class for counting repetitive events. It can return the number of
|
||||
* counts, the period of the most recent cycle, and detect when the signal being counted has
|
||||
* stopped by supplying a maximum cycle time.
|
||||
*
|
||||
* <p>All counters will immediately start counting - reset() them if you need them to be zeroed
|
||||
* before use.
|
||||
*/
|
||||
public class Counter extends SensorBase implements CounterBase, LiveWindowSendable, PIDSource {
|
||||
|
||||
/**
|
||||
* Mode determines how and what the counter counts.
|
||||
*/
|
||||
public enum Mode {
|
||||
/**
|
||||
* mode: two pulse.
|
||||
*/
|
||||
kTwoPulse(0),
|
||||
/**
|
||||
* mode: semi period.
|
||||
*/
|
||||
kSemiperiod(1),
|
||||
/**
|
||||
* mode: pulse length.
|
||||
*/
|
||||
kPulseLength(2),
|
||||
/**
|
||||
* mode: external direction.
|
||||
*/
|
||||
kExternalDirection(3);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private Mode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected DigitalSource m_upSource; // /< What makes the counter count up.
|
||||
protected DigitalSource m_downSource; // /< What makes the counter count down.
|
||||
private boolean m_allocatedUpSource;
|
||||
private boolean m_allocatedDownSource;
|
||||
private int m_counter; // /< The FPGA counter object.
|
||||
private int m_index; // /< The index of this counter.
|
||||
private PIDSourceType m_pidSource;
|
||||
private double m_distancePerPulse; // distance of travel for each tick
|
||||
|
||||
/**
|
||||
* Create an instance of a counter with the given mode.
|
||||
*/
|
||||
public Counter(final Mode mode) {
|
||||
ByteBuffer index = ByteBuffer.allocateDirect(4);
|
||||
// set the byte order
|
||||
index.order(ByteOrder.LITTLE_ENDIAN);
|
||||
m_counter = CounterJNI.initializeCounter(mode.value, index.asIntBuffer());
|
||||
m_index = index.asIntBuffer().get(0);
|
||||
|
||||
m_allocatedUpSource = false;
|
||||
m_allocatedDownSource = false;
|
||||
m_upSource = null;
|
||||
m_downSource = null;
|
||||
|
||||
setMaxPeriod(.5);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_Counter, m_index, mode.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a counter where no sources are selected. Then they all must be selected
|
||||
* by calling functions to specify the upsource and the downsource independently.
|
||||
*
|
||||
* <p>The counter will start counting immediately.
|
||||
*/
|
||||
public Counter() {
|
||||
this(Mode.kTwoPulse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a counter from a Digital Input. This is used if an existing digital input
|
||||
* is to be shared by multiple other objects such as encoders or if the Digital Source is not a
|
||||
* DIO channel (such as an Analog Trigger)
|
||||
*
|
||||
* <p>The counter will start counting immediately.
|
||||
*
|
||||
* @param source the digital source to count
|
||||
*/
|
||||
public Counter(DigitalSource source) {
|
||||
this();
|
||||
|
||||
requireNonNull(source, "Digital Source given was null");
|
||||
setUpSource(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a Counter object. Create an up-Counter instance given a channel.
|
||||
*
|
||||
* <p>The counter will start counting immediately.
|
||||
*
|
||||
* @param channel the DIO channel to use as the up source. 0-9 are on-board, 10-25 are on the MXP
|
||||
*/
|
||||
public Counter(int channel) {
|
||||
this();
|
||||
setUpSource(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a Counter object. Create an instance of a simple up-Counter given an
|
||||
* analog trigger. Use the trigger state output from the analog trigger.
|
||||
*
|
||||
* <p>The counter will start counting immediately.
|
||||
*
|
||||
* @param encodingType which edges to count
|
||||
* @param upSource first source to count
|
||||
* @param downSource second source for direction
|
||||
* @param inverted true to invert the count
|
||||
*/
|
||||
public Counter(EncodingType encodingType, DigitalSource upSource, DigitalSource downSource,
|
||||
boolean inverted) {
|
||||
this(Mode.kExternalDirection);
|
||||
|
||||
requireNonNull(encodingType, "Encoding type given was null");
|
||||
requireNonNull(upSource, "Up Source given was null");
|
||||
requireNonNull(downSource, "Down Source given was null");
|
||||
|
||||
if (encodingType != EncodingType.k1X && encodingType != EncodingType.k2X) {
|
||||
throw new RuntimeException("Counters only support 1X and 2X quadreature decoding!");
|
||||
}
|
||||
|
||||
setUpSource(upSource);
|
||||
setDownSource(downSource);
|
||||
|
||||
if (encodingType == EncodingType.k1X) {
|
||||
setUpSourceEdge(true, false);
|
||||
CounterJNI.setCounterAverageSize(m_counter, 1);
|
||||
} else {
|
||||
setUpSourceEdge(true, true);
|
||||
CounterJNI.setCounterAverageSize(m_counter, 2);
|
||||
}
|
||||
|
||||
setDownSourceEdge(inverted, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a Counter object. Create an instance of a simple up-Counter given an
|
||||
* analog trigger. Use the trigger state output from the analog trigger.
|
||||
*
|
||||
* <p>The counter will start counting immediately.
|
||||
*
|
||||
* @param trigger the analog trigger to count
|
||||
*/
|
||||
public Counter(AnalogTrigger trigger) {
|
||||
this();
|
||||
|
||||
requireNonNull(trigger, "The Analog Trigger given was null");
|
||||
|
||||
setUpSource(trigger.createOutput(AnalogTriggerType.kState));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
setUpdateWhenEmpty(true);
|
||||
|
||||
clearUpSource();
|
||||
clearDownSource();
|
||||
|
||||
CounterJNI.freeCounter(m_counter);
|
||||
|
||||
m_upSource = null;
|
||||
m_downSource = null;
|
||||
m_counter = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The counter's FPGA index.
|
||||
*
|
||||
* @return the Counter's FPGA index
|
||||
*/
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public int getFPGAIndex() {
|
||||
return m_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upsource for the counter as a digital input channel.
|
||||
*
|
||||
* @param channel the DIO channel to count 0-9 are on-board, 10-25 are on the MXP
|
||||
*/
|
||||
public void setUpSource(int channel) {
|
||||
setUpSource(new DigitalInput(channel));
|
||||
m_allocatedUpSource = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source object that causes the counter to count up. Set the up counting DigitalSource.
|
||||
*
|
||||
* @param source the digital source to count
|
||||
*/
|
||||
public void setUpSource(DigitalSource source) {
|
||||
if (m_upSource != null && m_allocatedUpSource) {
|
||||
m_upSource.free();
|
||||
m_allocatedUpSource = false;
|
||||
}
|
||||
m_upSource = source;
|
||||
CounterJNI.setCounterUpSource(m_counter, source.getPortHandleForRouting(),
|
||||
source.getAnalogTriggerTypeForRouting());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the up counting source to be an analog trigger.
|
||||
*
|
||||
* @param analogTrigger The analog trigger object that is used for the Up Source
|
||||
* @param triggerType The analog trigger output that will trigger the counter.
|
||||
*/
|
||||
public void setUpSource(AnalogTrigger analogTrigger, AnalogTriggerType triggerType) {
|
||||
requireNonNull(analogTrigger, "Analog Trigger given was null");
|
||||
requireNonNull(triggerType, "Analog Trigger Type given was null");
|
||||
|
||||
setUpSource(analogTrigger.createOutput(triggerType));
|
||||
m_allocatedUpSource = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the edge sensitivity on an up counting source. Set the up source to either detect rising
|
||||
* edges or falling edges.
|
||||
*
|
||||
* @param risingEdge true to count rising edge
|
||||
* @param fallingEdge true to count falling edge
|
||||
*/
|
||||
public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
|
||||
if (m_upSource == null) {
|
||||
throw new RuntimeException("Up Source must be set before setting the edge!");
|
||||
}
|
||||
CounterJNI.setCounterUpSourceEdge(m_counter, risingEdge, fallingEdge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the up counting source to the counter.
|
||||
*/
|
||||
public void clearUpSource() {
|
||||
if (m_upSource != null && m_allocatedUpSource) {
|
||||
m_upSource.free();
|
||||
m_allocatedUpSource = false;
|
||||
}
|
||||
m_upSource = null;
|
||||
|
||||
CounterJNI.clearCounterUpSource(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the down counting source to be a digital input channel.
|
||||
*
|
||||
* @param channel the DIO channel to count 0-9 are on-board, 10-25 are on the MXP
|
||||
*/
|
||||
public void setDownSource(int channel) {
|
||||
setDownSource(new DigitalInput(channel));
|
||||
m_allocatedDownSource = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source object that causes the counter to count down. Set the down counting
|
||||
* DigitalSource.
|
||||
*
|
||||
* @param source the digital source to count
|
||||
*/
|
||||
public void setDownSource(DigitalSource source) {
|
||||
requireNonNull(source, "The Digital Source given was null");
|
||||
|
||||
if (m_downSource != null && m_allocatedDownSource) {
|
||||
m_downSource.free();
|
||||
m_allocatedDownSource = false;
|
||||
}
|
||||
CounterJNI.setCounterDownSource(m_counter, source.getPortHandleForRouting(),
|
||||
source.getAnalogTriggerTypeForRouting());
|
||||
m_downSource = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the down counting source to be an analog trigger.
|
||||
*
|
||||
* @param analogTrigger The analog trigger object that is used for the Down Source
|
||||
* @param triggerType The analog trigger output that will trigger the counter.
|
||||
*/
|
||||
public void setDownSource(AnalogTrigger analogTrigger, AnalogTriggerType triggerType) {
|
||||
requireNonNull(analogTrigger, "Analog Trigger given was null");
|
||||
requireNonNull(triggerType, "Analog Trigger Type given was null");
|
||||
|
||||
setDownSource(analogTrigger.createOutput(triggerType));
|
||||
m_allocatedDownSource = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the edge sensitivity on a down counting source. Set the down source to either detect rising
|
||||
* edges or falling edges.
|
||||
*
|
||||
* @param risingEdge true to count the rising edge
|
||||
* @param fallingEdge true to count the falling edge
|
||||
*/
|
||||
public void setDownSourceEdge(boolean risingEdge, boolean fallingEdge) {
|
||||
requireNonNull(m_downSource, "Down Source must be set before setting the edge!");
|
||||
|
||||
CounterJNI.setCounterDownSourceEdge(m_counter, risingEdge, fallingEdge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the down counting source to the counter.
|
||||
*/
|
||||
public void clearDownSource() {
|
||||
if (m_downSource != null && m_allocatedDownSource) {
|
||||
m_downSource.free();
|
||||
m_allocatedDownSource = false;
|
||||
}
|
||||
m_downSource = null;
|
||||
|
||||
CounterJNI.clearCounterDownSource(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set standard up / down counting mode on this counter. Up and down counts are sourced
|
||||
* independently from two inputs.
|
||||
*/
|
||||
public void setUpDownCounterMode() {
|
||||
CounterJNI.setCounterUpDownMode(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set external direction mode on this counter. Counts are sourced on the Up counter input. The
|
||||
* Down counter input represents the direction to count.
|
||||
*/
|
||||
public void setExternalDirectionMode() {
|
||||
CounterJNI.setCounterExternalDirectionMode(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Semi-period mode on this counter. Counts up on both rising and falling edges.
|
||||
*
|
||||
* @param highSemiPeriod true to count up on both rising and falling
|
||||
*/
|
||||
public void setSemiPeriodMode(boolean highSemiPeriod) {
|
||||
CounterJNI.setCounterSemiPeriodMode(m_counter, highSemiPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the counter to count in up or down based on the length of the input pulse. This mode
|
||||
* is most useful for direction sensitive gear tooth sensors.
|
||||
*
|
||||
* @param threshold The pulse length beyond which the counter counts the opposite direction. Units
|
||||
* are seconds.
|
||||
*/
|
||||
public void setPulseLengthMode(double threshold) {
|
||||
CounterJNI.setCounterPulseLengthMode(m_counter, threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the current counter value. Read the value at this instant. It may still be running, so it
|
||||
* reflects the current value. Next time it is read, it might have a different value.
|
||||
*/
|
||||
@Override
|
||||
public int get() {
|
||||
return CounterJNI.getCounter(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the current scaled counter value. Read the value at this instant, scaled by the distance
|
||||
* per pulse (defaults to 1).
|
||||
*
|
||||
* @return The distance since the last reset
|
||||
*/
|
||||
public double getDistance() {
|
||||
return get() * m_distancePerPulse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the Counter to zero. Set the counter value to zero. This doesn't effect the running state
|
||||
* of the counter, just sets the current value to zero.
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
CounterJNI.resetCounter(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum period where the device is still considered "moving". Sets the maximum period
|
||||
* where the device is considered moving. This value is used to determine the "stopped" state of
|
||||
* the counter using the GetStopped method.
|
||||
*
|
||||
* @param maxPeriod The maximum period where the counted device is considered moving in seconds.
|
||||
*/
|
||||
@Override
|
||||
public void setMaxPeriod(double maxPeriod) {
|
||||
CounterJNI.setCounterMaxPeriod(m_counter, maxPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select whether you want to continue updating the event timer output when there are no samples
|
||||
* captured. The output of the event timer has a buffer of periods that are averaged and posted to
|
||||
* a register on the FPGA. When the timer detects that the event source has stopped (based on the
|
||||
* MaxPeriod) the buffer of samples to be averaged is emptied. If you enable the update when
|
||||
* empty, you will be notified of the stopped source and the event time will report 0 samples. If
|
||||
* you disable update when empty, the most recent average will remain on the output until a new
|
||||
* sample is acquired. You will never see 0 samples output (except when there have been no events
|
||||
* since an FPGA reset) and you will likely not see the stopped bit become true (since it is
|
||||
* updated at the end of an average and there are no samples to average).
|
||||
*
|
||||
* @param enabled true to continue updating
|
||||
*/
|
||||
public void setUpdateWhenEmpty(boolean enabled) {
|
||||
CounterJNI.setCounterUpdateWhenEmpty(m_counter, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the clock is stopped. Determine if the clocked input is stopped based on the
|
||||
* MaxPeriod value set using the SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the
|
||||
* device (and counter) are assumed to be stopped and it returns true.
|
||||
*
|
||||
* @return true if the most recent counter period exceeds the MaxPeriod value set by SetMaxPeriod.
|
||||
*/
|
||||
@Override
|
||||
public boolean getStopped() {
|
||||
return CounterJNI.getCounterStopped(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* The last direction the counter value changed.
|
||||
*
|
||||
* @return The last direction the counter value changed.
|
||||
*/
|
||||
@Override
|
||||
public boolean getDirection() {
|
||||
return CounterJNI.getCounterDirection(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Counter to return reversed sensing on the direction. This allows counters to change the
|
||||
* direction they are counting in the case of 1X and 2X quadrature encoding only. Any other
|
||||
* counter mode isn't supported.
|
||||
*
|
||||
* @param reverseDirection true if the value counted should be negated.
|
||||
*/
|
||||
public void setReverseDirection(boolean reverseDirection) {
|
||||
CounterJNI.setCounterReverseDirection(m_counter, reverseDirection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Period of the most recent count. Returns the time interval of the most recent count.
|
||||
* This can be used for velocity calculations to determine shaft speed.
|
||||
*
|
||||
* @return The period of the last two pulses in units of seconds.
|
||||
*/
|
||||
@Override
|
||||
public double getPeriod() {
|
||||
return CounterJNI.getCounterPeriod(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current rate of the Counter. Read the current rate of the counter accounting for the
|
||||
* distance per pulse value. The default value for distance per pulse (1) yields units of pulses
|
||||
* per second.
|
||||
*
|
||||
* @return The rate in units/sec
|
||||
*/
|
||||
public double getRate() {
|
||||
return m_distancePerPulse / getPeriod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Samples to Average which specifies the number of samples of the timer to average when
|
||||
* calculating the period. Perform averaging to account for mechanical imperfections or as
|
||||
* oversampling to increase resolution.
|
||||
*
|
||||
* @param samplesToAverage The number of samples to average from 1 to 127.
|
||||
*/
|
||||
public void setSamplesToAverage(int samplesToAverage) {
|
||||
CounterJNI.setCounterSamplesToAverage(m_counter, samplesToAverage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Samples to Average which specifies the number of samples of the timer to average when
|
||||
* calculating the period. Perform averaging to account for mechanical imperfections or as
|
||||
* oversampling to increase resolution.
|
||||
*
|
||||
* @return SamplesToAverage The number of samples being averaged (from 1 to 127)
|
||||
*/
|
||||
public int getSamplesToAverage() {
|
||||
return CounterJNI.getCounterSamplesToAverage(m_counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the distance per pulse for this counter. This sets the multiplier used to determine the
|
||||
* distance driven based on the count value from the encoder. Set this value based on the Pulses
|
||||
* per Revolution and factor in any gearing reductions. This distance can be in any units you
|
||||
* like, linear or angular.
|
||||
*
|
||||
* @param distancePerPulse The scale factor that will be used to convert pulses to useful units.
|
||||
*/
|
||||
public void setDistancePerPulse(double distancePerPulse) {
|
||||
m_distancePerPulse = distancePerPulse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which parameter of the encoder you are using as a process control variable. The counter
|
||||
* class supports the rate and distance parameters.
|
||||
*
|
||||
* @param pidSource An enum to select the parameter.
|
||||
*/
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
requireNonNull(pidSource, "PID Source Parameter given was null");
|
||||
if (pidSource != PIDSourceType.kDisplacement && pidSource != PIDSourceType.kRate) {
|
||||
throw new IllegalArgumentException("PID Source parameter was not valid type: " + pidSource);
|
||||
}
|
||||
|
||||
m_pidSource = pidSource;
|
||||
}
|
||||
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return m_pidSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double pidGet() {
|
||||
switch (m_pidSource) {
|
||||
case kDisplacement:
|
||||
return getDistance();
|
||||
case kRate:
|
||||
return getRate();
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Counter";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
84
wpilibj/src/main/java/edu/wpi/first/wpilibj/CounterBase.java
Normal file
84
wpilibj/src/main/java/edu/wpi/first/wpilibj/CounterBase.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* Interface for counting the number of ticks on a digital input channel. Encoders, Gear tooth
|
||||
* sensors, and counters should all subclass this so it can be used to build more advanced classes
|
||||
* for control and driving.
|
||||
*
|
||||
* <p>All counters will immediately start counting - reset() them if you need them to be zeroed
|
||||
* before use.
|
||||
*/
|
||||
public interface CounterBase {
|
||||
|
||||
/**
|
||||
* The number of edges for the counterbase to increment or decrement on.
|
||||
*/
|
||||
enum EncodingType {
|
||||
/**
|
||||
* Count only the rising edge.
|
||||
*/
|
||||
k1X(0),
|
||||
/**
|
||||
* Count both the rising and falling edge.
|
||||
*/
|
||||
k2X(1),
|
||||
/**
|
||||
* Count rising and falling on both channels.
|
||||
*/
|
||||
k4X(2);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private EncodingType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count.
|
||||
*
|
||||
* @return the count
|
||||
*/
|
||||
int get();
|
||||
|
||||
/**
|
||||
* Reset the count to zero.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Get the time between the last two edges counted.
|
||||
*
|
||||
* @return the time beteween the last two ticks in seconds
|
||||
*/
|
||||
double getPeriod();
|
||||
|
||||
/**
|
||||
* Set the maximum time between edges to be considered stalled.
|
||||
*
|
||||
* @param maxPeriod the maximum period in seconds
|
||||
*/
|
||||
void setMaxPeriod(double maxPeriod);
|
||||
|
||||
/**
|
||||
* Determine if the counter is not moving.
|
||||
*
|
||||
* @return true if the counter has not changed for the max period
|
||||
*/
|
||||
boolean getStopped();
|
||||
|
||||
/**
|
||||
* Determine which direction the counter is going.
|
||||
*
|
||||
* @return true for one direction, false for the other
|
||||
*/
|
||||
boolean getDirection();
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2017. 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 java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.DigitalGlitchFilterJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
|
||||
/**
|
||||
* Class to enable glitch filtering on a set of digital inputs. This class will manage adding and
|
||||
* removing digital inputs from a FPGA glitch filter. The filter lets the user configure the time
|
||||
* that an input must remain high or low before it is classified as high or low.
|
||||
*/
|
||||
public class DigitalGlitchFilter extends SensorBase {
|
||||
|
||||
/**
|
||||
* Configures the Digital Glitch Filter to its default settings.
|
||||
*/
|
||||
public DigitalGlitchFilter() {
|
||||
synchronized (m_mutex) {
|
||||
int index = 0;
|
||||
while (m_filterAllocated[index] && index < m_filterAllocated.length) {
|
||||
index++;
|
||||
}
|
||||
if (index != m_filterAllocated.length) {
|
||||
m_channelIndex = index;
|
||||
m_filterAllocated[index] = true;
|
||||
HAL.report(tResourceType.kResourceType_DigitalFilter,
|
||||
m_channelIndex, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the resources used by this object.
|
||||
*/
|
||||
public void free() {
|
||||
if (m_channelIndex >= 0) {
|
||||
synchronized (m_mutex) {
|
||||
m_filterAllocated[m_channelIndex] = false;
|
||||
}
|
||||
m_channelIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void setFilter(DigitalSource input, int channelIndex) {
|
||||
if (input != null) { // Counter might have just one input
|
||||
// analog triggers are not supported for DigitalGlitchFilters
|
||||
if (input.isAnalogTrigger()) {
|
||||
throw new IllegalStateException("Analog Triggers not supported for DigitalGlitchFilters");
|
||||
}
|
||||
DigitalGlitchFilterJNI.setFilterSelect(input.getPortHandleForRouting(), channelIndex);
|
||||
|
||||
int selected = DigitalGlitchFilterJNI.getFilterSelect(input.getPortHandleForRouting());
|
||||
if (selected != channelIndex) {
|
||||
throw new IllegalStateException("DigitalGlitchFilterJNI.setFilterSelect("
|
||||
+ channelIndex + ") failed -> " + selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the DigitalSource to this glitch filter.
|
||||
*
|
||||
* @param input The DigitalSource to add.
|
||||
*/
|
||||
public void add(DigitalSource input) {
|
||||
setFilter(input, m_channelIndex + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the Encoder to this glitch filter.
|
||||
*
|
||||
* @param input The Encoder to add.
|
||||
*/
|
||||
public void add(Encoder input) {
|
||||
add(input.m_aSource);
|
||||
add(input.m_bSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the Counter to this glitch filter.
|
||||
*
|
||||
* @param input The Counter to add.
|
||||
*/
|
||||
public void add(Counter input) {
|
||||
add(input.m_upSource);
|
||||
add(input.m_downSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this filter from the given digital input.
|
||||
*
|
||||
* @param input The DigitalSource to stop filtering.
|
||||
*/
|
||||
public void remove(DigitalSource input) {
|
||||
setFilter(input, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this filter from the given Encoder.
|
||||
*
|
||||
* @param input the Encoder to stop filtering.
|
||||
*/
|
||||
public void remove(Encoder input) {
|
||||
remove(input.m_aSource);
|
||||
remove(input.m_bSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this filter from the given Counter.
|
||||
*
|
||||
* @param input The Counter to stop filtering.
|
||||
*/
|
||||
public void remove(Counter input) {
|
||||
remove(input.m_upSource);
|
||||
remove(input.m_downSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of FPGA cycles that the input must hold steady to pass through this glitch
|
||||
* filter.
|
||||
*
|
||||
* @param fpgaCycles The number of FPGA cycles.
|
||||
*/
|
||||
public void setPeriodCycles(int fpgaCycles) {
|
||||
DigitalGlitchFilterJNI.setFilterPeriod(m_channelIndex, fpgaCycles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of nanoseconds that the input must hold steady to pass through this glitch
|
||||
* filter.
|
||||
*
|
||||
* @param nanoseconds The number of nanoseconds.
|
||||
*/
|
||||
public void setPeriodNanoSeconds(long nanoseconds) {
|
||||
int fpgaCycles = (int) (nanoseconds * kSystemClockTicksPerMicrosecond / 4
|
||||
/ 1000);
|
||||
setPeriodCycles(fpgaCycles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of FPGA cycles that the input must hold steady to pass through this glitch
|
||||
* filter.
|
||||
*
|
||||
* @return The number of cycles.
|
||||
*/
|
||||
public int getPeriodCycles() {
|
||||
return DigitalGlitchFilterJNI.getFilterPeriod(m_channelIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of nanoseconds that the input must hold steady to pass through this glitch
|
||||
* filter.
|
||||
*
|
||||
* @return The number of nanoseconds.
|
||||
*/
|
||||
public long getPeriodNanoSeconds() {
|
||||
int fpgaCycles = getPeriodCycles();
|
||||
|
||||
return (long) fpgaCycles * 1000L
|
||||
/ (long) (kSystemClockTicksPerMicrosecond / 4);
|
||||
}
|
||||
|
||||
private int m_channelIndex = -1;
|
||||
private static final Lock m_mutex = new ReentrantLock(true);
|
||||
private static final boolean[] m_filterAllocated = new boolean[3];
|
||||
}
|
||||
140
wpilibj/src/main/java/edu/wpi/first/wpilibj/DigitalInput.java
Normal file
140
wpilibj/src/main/java/edu/wpi/first/wpilibj/DigitalInput.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.DIOJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* Class to read a digital input. This class will read digital inputs and return the current value
|
||||
* on the channel. Other devices such as encoders, gear tooth sensors, etc. that are implemented
|
||||
* elsewhere will automatically allocate digital inputs and outputs as required. This class is only
|
||||
* for devices like switches etc. that aren't implemented anywhere else.
|
||||
*/
|
||||
public class DigitalInput extends DigitalSource implements LiveWindowSendable {
|
||||
private int m_channel = 0;
|
||||
private int m_handle = 0;
|
||||
|
||||
/**
|
||||
* Create an instance of a Digital Input class. Creates a digital input given a channel.
|
||||
*
|
||||
* @param channel the DIO channel for the digital input 0-9 are on-board, 10-25 are on the MXP
|
||||
*/
|
||||
public DigitalInput(int channel) {
|
||||
checkDigitalChannel(channel);
|
||||
m_channel = channel;
|
||||
|
||||
m_handle = DIOJNI.initializeDIOPort(DIOJNI.getPort((byte) channel), true);
|
||||
|
||||
LiveWindow.addSensor("DigitalInput", channel, this);
|
||||
HAL.report(tResourceType.kResourceType_DigitalInput, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the resources for this output.
|
||||
*/
|
||||
public void free() {
|
||||
if (m_interrupt != 0) {
|
||||
cancelInterrupts();
|
||||
}
|
||||
|
||||
DIOJNI.freeDIOPort(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value from a digital input channel. Retrieve the value of a single digital input
|
||||
* channel from the FPGA.
|
||||
*
|
||||
* @return the status of the digital input
|
||||
*/
|
||||
public boolean get() {
|
||||
return DIOJNI.getDIO(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channel of the digital input.
|
||||
*
|
||||
* @return The GPIO channel number that this object represents.
|
||||
*/
|
||||
@Override
|
||||
public int getChannel() {
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the analog trigger type.
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
@Override
|
||||
public int getAnalogTriggerTypeForRouting() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an analog trigger.
|
||||
*
|
||||
* @return true if this is an analog trigger
|
||||
*/
|
||||
@Override
|
||||
public boolean isAnalogTrigger() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HAL Port Handle.
|
||||
*
|
||||
* @return The HAL Handle to the specified source.
|
||||
*/
|
||||
@Override
|
||||
public int getPortHandleForRouting() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Digital Input";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putBoolean("Value", get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
242
wpilibj/src/main/java/edu/wpi/first/wpilibj/DigitalOutput.java
Normal file
242
wpilibj/src/main/java/edu/wpi/first/wpilibj/DigitalOutput.java
Normal file
@@ -0,0 +1,242 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.DIOJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
/**
|
||||
* Class to write digital outputs. This class will write digital outputs. Other devices that are
|
||||
* implemented elsewhere will automatically allocate digital inputs and outputs as required.
|
||||
*/
|
||||
public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
|
||||
|
||||
private static final int invalidPwmGenerator = 0;
|
||||
private int m_pwmGenerator = invalidPwmGenerator;
|
||||
|
||||
private int m_channel = 0;
|
||||
private int m_handle = 0;
|
||||
|
||||
/**
|
||||
* Create an instance of a digital output. Create an instance of a digital output given a
|
||||
* channel.
|
||||
*
|
||||
* @param channel the DIO channel to use for the digital output. 0-9 are on-board, 10-25 are on
|
||||
* the MXP
|
||||
*/
|
||||
public DigitalOutput(int channel) {
|
||||
checkDigitalChannel(channel);
|
||||
m_channel = channel;
|
||||
|
||||
m_handle = DIOJNI.initializeDIOPort(DIOJNI.getPort((byte) channel), false);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_DigitalOutput, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the resources associated with a digital output.
|
||||
*/
|
||||
@Override
|
||||
public void free() {
|
||||
// disable the pwm only if we have allocated it
|
||||
if (m_pwmGenerator != invalidPwmGenerator) {
|
||||
disablePWM();
|
||||
}
|
||||
DIOJNI.freeDIOPort(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a digital output.
|
||||
*
|
||||
* @param value true is on, off is false
|
||||
*/
|
||||
public void set(boolean value) {
|
||||
DIOJNI.setDIO(m_handle, (short) (value ? 1 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value being output from the Digital Output.
|
||||
*
|
||||
* @return the state of the digital output.
|
||||
*/
|
||||
public boolean get() {
|
||||
return DIOJNI.getDIO(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the GPIO channel number that this object represents.
|
||||
*
|
||||
* @return The GPIO channel number.
|
||||
*/
|
||||
@Override
|
||||
public int getChannel() {
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a single pulse. There can only be a single pulse going at any time.
|
||||
*
|
||||
* @param pulseLength The length of the pulse.
|
||||
*/
|
||||
public void pulse(final double pulseLength) {
|
||||
DIOJNI.pulse(m_handle, pulseLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the pulse is still going. Determine if a previously started pulse is still going.
|
||||
*
|
||||
* @return true if pulsing
|
||||
*/
|
||||
public boolean isPulsing() {
|
||||
return DIOJNI.isPulsing(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the PWM frequency of the PWM output on a Digital Output line.
|
||||
*
|
||||
* <p>The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
|
||||
*
|
||||
* <p>There is only one PWM frequency for all channnels.
|
||||
*
|
||||
* @param rate The frequency to output all digital output PWM signals.
|
||||
*/
|
||||
public void setPWMRate(double rate) {
|
||||
DIOJNI.setDigitalPWMRate(rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a PWM Output on this line.
|
||||
*
|
||||
* <p>Allocate one of the 6 DO PWM generator resources.
|
||||
*
|
||||
* <p>Supply the initial duty-cycle to output so as to avoid a glitch when first starting.
|
||||
*
|
||||
* <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
|
||||
* the higher the frequency of the PWM signal is.
|
||||
*
|
||||
* @param initialDutyCycle The duty-cycle to start generating. [0..1]
|
||||
*/
|
||||
public void enablePWM(double initialDutyCycle) {
|
||||
if (m_pwmGenerator != invalidPwmGenerator) {
|
||||
return;
|
||||
}
|
||||
m_pwmGenerator = DIOJNI.allocateDigitalPWM();
|
||||
DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle);
|
||||
DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change this line from a PWM output back to a static Digital Output line.
|
||||
*
|
||||
* <p>Free up one of the 6 DO PWM generator resources that were in use.
|
||||
*/
|
||||
public void disablePWM() {
|
||||
if (m_pwmGenerator == invalidPwmGenerator) {
|
||||
return;
|
||||
}
|
||||
// Disable the output by routing to a dead bit.
|
||||
DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, kDigitalChannels);
|
||||
DIOJNI.freeDigitalPWM(m_pwmGenerator);
|
||||
m_pwmGenerator = invalidPwmGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the duty-cycle that is being generated on the line.
|
||||
*
|
||||
* <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
|
||||
* the
|
||||
* higher the frequency of the PWM signal is.
|
||||
*
|
||||
* @param dutyCycle The duty-cycle to change to. [0..1]
|
||||
*/
|
||||
public void updateDutyCycle(double dutyCycle) {
|
||||
if (m_pwmGenerator == invalidPwmGenerator) {
|
||||
return;
|
||||
}
|
||||
DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the analog trigger type.
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
@Override
|
||||
public int getAnalogTriggerTypeForRouting() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an analog trigger.
|
||||
*
|
||||
* @return true if this is an analog trigger
|
||||
*/
|
||||
@Override
|
||||
public boolean isAnalogTrigger() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HAL Port Handle.
|
||||
*
|
||||
* @return The HAL Handle to the specified source.
|
||||
*/
|
||||
@Override
|
||||
public int getPortHandleForRouting() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Digital Output";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
private ITableListener m_tableListener;
|
||||
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
// TODO: Put current value.
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
m_tableListener = (source, key, value, isNew) -> set((boolean) value);
|
||||
m_table.addTableListener("Value", m_tableListener, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
// TODO: Broken, should only remove the listener from "Value" only.
|
||||
m_table.removeTableListener(m_tableListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* DigitalSource Interface. The DigitalSource represents all the possible inputs for a counter or a
|
||||
* quadrature encoder. The source may be either a digital input or an analog input. If the caller
|
||||
* just provides a channel, then a digital input will be constructed and freed when finished for the
|
||||
* source. The source can either be a digital input or analog trigger but not both.
|
||||
*/
|
||||
public abstract class DigitalSource extends InterruptableSensorBase {
|
||||
public abstract boolean isAnalogTrigger();
|
||||
|
||||
public abstract int getChannel();
|
||||
}
|
||||
220
wpilibj/src/main/java/edu/wpi/first/wpilibj/DoubleSolenoid.java
Normal file
220
wpilibj/src/main/java/edu/wpi/first/wpilibj/DoubleSolenoid.java
Normal file
@@ -0,0 +1,220 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.hal.SolenoidJNI;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
/**
|
||||
* DoubleSolenoid class for running 2 channels of high voltage Digital Output on the PCM.
|
||||
*
|
||||
* <p>The DoubleSolenoid class is typically used for pneumatics solenoids that have two positions
|
||||
* controlled by two separate channels.
|
||||
*/
|
||||
public class DoubleSolenoid extends SolenoidBase implements LiveWindowSendable {
|
||||
|
||||
/**
|
||||
* Possible values for a DoubleSolenoid.
|
||||
*/
|
||||
public enum Value {
|
||||
kOff,
|
||||
kForward,
|
||||
kReverse
|
||||
}
|
||||
|
||||
private byte m_forwardMask; // The mask for the forward channel.
|
||||
private byte m_reverseMask; // The mask for the reverse channel.
|
||||
private int m_forwardHandle = 0;
|
||||
private int m_reverseHandle = 0;
|
||||
|
||||
/**
|
||||
* Constructor. Uses the default PCM ID (defaults to 0).
|
||||
*
|
||||
* @param forwardChannel The forward channel number on the PCM (0..7).
|
||||
* @param reverseChannel The reverse channel number on the PCM (0..7).
|
||||
*/
|
||||
public DoubleSolenoid(final int forwardChannel, final int reverseChannel) {
|
||||
this(getDefaultSolenoidModule(), forwardChannel, reverseChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param moduleNumber The module number of the solenoid module to use.
|
||||
* @param forwardChannel The forward channel on the module to control (0..7).
|
||||
* @param reverseChannel The reverse channel on the module to control (0..7).
|
||||
*/
|
||||
public DoubleSolenoid(final int moduleNumber, final int forwardChannel,
|
||||
final int reverseChannel) {
|
||||
super(moduleNumber);
|
||||
|
||||
checkSolenoidModule(m_moduleNumber);
|
||||
checkSolenoidChannel(forwardChannel);
|
||||
checkSolenoidChannel(reverseChannel);
|
||||
|
||||
int portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) forwardChannel);
|
||||
m_forwardHandle = SolenoidJNI.initializeSolenoidPort(portHandle);
|
||||
|
||||
try {
|
||||
portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) reverseChannel);
|
||||
m_reverseHandle = SolenoidJNI.initializeSolenoidPort(portHandle);
|
||||
} catch (RuntimeException ex) {
|
||||
// free the forward handle on exception, then rethrow
|
||||
SolenoidJNI.freeSolenoidPort(m_forwardHandle);
|
||||
m_forwardHandle = 0;
|
||||
m_reverseHandle = 0;
|
||||
throw ex;
|
||||
}
|
||||
|
||||
m_forwardMask = (byte) (1 << forwardChannel);
|
||||
m_reverseMask = (byte) (1 << reverseChannel);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_Solenoid, forwardChannel,
|
||||
m_moduleNumber);
|
||||
HAL.report(tResourceType.kResourceType_Solenoid, reverseChannel,
|
||||
m_moduleNumber);
|
||||
LiveWindow.addActuator("DoubleSolenoid", m_moduleNumber, forwardChannel, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
public synchronized void free() {
|
||||
SolenoidJNI.freeSolenoidPort(m_forwardHandle);
|
||||
SolenoidJNI.freeSolenoidPort(m_reverseHandle);
|
||||
super.free();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a solenoid.
|
||||
*
|
||||
* @param value The value to set (Off, Forward, Reverse)
|
||||
*/
|
||||
public void set(final Value value) {
|
||||
boolean forward = false;
|
||||
boolean reverse = false;
|
||||
|
||||
switch (value) {
|
||||
case kOff:
|
||||
forward = false;
|
||||
reverse = false;
|
||||
break;
|
||||
case kForward:
|
||||
forward = true;
|
||||
reverse = false;
|
||||
break;
|
||||
case kReverse:
|
||||
forward = false;
|
||||
reverse = true;
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Illegal value: " + value);
|
||||
|
||||
}
|
||||
|
||||
SolenoidJNI.setSolenoid(m_forwardHandle, forward);
|
||||
SolenoidJNI.setSolenoid(m_reverseHandle, reverse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the current value of the solenoid.
|
||||
*
|
||||
* @return The current value of the solenoid.
|
||||
*/
|
||||
public Value get() {
|
||||
boolean valueForward = SolenoidJNI.getSolenoid(m_forwardHandle);
|
||||
boolean valueReverse = SolenoidJNI.getSolenoid(m_reverseHandle);
|
||||
|
||||
if (valueForward) {
|
||||
return Value.kForward;
|
||||
}
|
||||
if (valueReverse) {
|
||||
return Value.kReverse;
|
||||
}
|
||||
return Value.kOff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the forward solenoid is blacklisted. If a solenoid is shorted, it is added to the
|
||||
* blacklist and disabled until power cycle, or until faults are cleared.
|
||||
*
|
||||
* @return If solenoid is disabled due to short.
|
||||
* @see #clearAllPCMStickyFaults()
|
||||
*/
|
||||
public boolean isFwdSolenoidBlackListed() {
|
||||
int blackList = getPCMSolenoidBlackList();
|
||||
return (blackList & m_forwardMask) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the reverse solenoid is blacklisted. If a solenoid is shorted, it is added to the
|
||||
* blacklist and disabled until power cycle, or until faults are cleared.
|
||||
*
|
||||
* @return If solenoid is disabled due to short.
|
||||
* @see #clearAllPCMStickyFaults()
|
||||
*/
|
||||
public boolean isRevSolenoidBlackListed() {
|
||||
int blackList = getPCMSolenoidBlackList();
|
||||
return (blackList & m_reverseMask) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
public String getSmartDashboardType() {
|
||||
return "Double Solenoid";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
private ITableListener m_tableListener;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putString("Value", get().name().substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
set(Value.kOff); // Stop for safety
|
||||
m_tableListener = (source, key, value, isNew) -> {
|
||||
if ("Reverse".equals(value.toString())) {
|
||||
set(Value.kReverse);
|
||||
} else if ("Forward".equals(value.toString())) {
|
||||
set(Value.kForward);
|
||||
} else {
|
||||
set(Value.kOff);
|
||||
}
|
||||
};
|
||||
m_table.addTableListener("Value", m_tableListener, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
set(Value.kOff); // Stop for safety
|
||||
// TODO: Broken, should only remove the listener from "Value" only.
|
||||
m_table.removeTableListener(m_tableListener);
|
||||
}
|
||||
}
|
||||
802
wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java
Normal file
802
wpilibj/src/main/java/edu/wpi/first/wpilibj/DriverStation.java
Normal file
@@ -0,0 +1,802 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.nio.ByteBuffer;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.AllianceStationID;
|
||||
import edu.wpi.first.wpilibj.hal.ControlWord;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.hal.PowerJNI;
|
||||
|
||||
/**
|
||||
* Provide access to the network communication data to / from the Driver Station.
|
||||
*/
|
||||
public class DriverStation implements RobotState.Interface {
|
||||
|
||||
/**
|
||||
* Number of Joystick Ports.
|
||||
*/
|
||||
public static final int kJoystickPorts = 6;
|
||||
|
||||
private class HALJoystickButtons {
|
||||
public int m_buttons;
|
||||
public byte m_count;
|
||||
}
|
||||
|
||||
private class HALJoystickAxes {
|
||||
public float[] m_axes;
|
||||
public short m_count;
|
||||
|
||||
public HALJoystickAxes(int count) {
|
||||
m_axes = new float[count];
|
||||
}
|
||||
}
|
||||
|
||||
private class HALJoystickPOVs {
|
||||
public short[] m_povs;
|
||||
public short m_count;
|
||||
|
||||
public HALJoystickPOVs(int count) {
|
||||
m_povs = new short[count];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The robot alliance that the robot is a part of.
|
||||
*/
|
||||
public enum Alliance {
|
||||
Red, Blue, Invalid
|
||||
}
|
||||
|
||||
private static final double JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL = 1.0;
|
||||
private double m_nextMessageTime = 0.0;
|
||||
|
||||
private static class DriverStationTask implements Runnable {
|
||||
|
||||
private DriverStation m_ds;
|
||||
|
||||
DriverStationTask(DriverStation ds) {
|
||||
m_ds = ds;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
m_ds.run();
|
||||
}
|
||||
} /* DriverStationTask */
|
||||
|
||||
private static DriverStation instance = new DriverStation();
|
||||
|
||||
// Joystick User Data
|
||||
private HALJoystickAxes[] m_joystickAxes = new HALJoystickAxes[kJoystickPorts];
|
||||
private HALJoystickPOVs[] m_joystickPOVs = new HALJoystickPOVs[kJoystickPorts];
|
||||
private HALJoystickButtons[] m_joystickButtons = new HALJoystickButtons[kJoystickPorts];
|
||||
|
||||
// Joystick Cached Data
|
||||
private HALJoystickAxes[] m_joystickAxesCache = new HALJoystickAxes[kJoystickPorts];
|
||||
private HALJoystickPOVs[] m_joystickPOVsCache = new HALJoystickPOVs[kJoystickPorts];
|
||||
private HALJoystickButtons[] m_joystickButtonsCache = new HALJoystickButtons[kJoystickPorts];
|
||||
// preallocated byte buffer for button count
|
||||
private ByteBuffer m_buttonCountBuffer = ByteBuffer.allocateDirect(1);
|
||||
|
||||
// Internal Driver Station thread
|
||||
private Thread m_thread;
|
||||
private volatile boolean m_threadKeepAlive = true;
|
||||
|
||||
private final Object m_joystickMutex;
|
||||
|
||||
// Robot state status variables
|
||||
private boolean m_userInDisabled = false;
|
||||
private boolean m_userInAutonomous = false;
|
||||
private boolean m_userInTeleop = false;
|
||||
private boolean m_userInTest = false;
|
||||
|
||||
// Control word variables
|
||||
private final Object m_controlWordMutex;
|
||||
private ControlWord m_controlWordCache;
|
||||
private long m_lastControlWordUpdate;
|
||||
|
||||
/**
|
||||
* Gets an instance of the DriverStation.
|
||||
*
|
||||
* @return The DriverStation.
|
||||
*/
|
||||
public static DriverStation getInstance() {
|
||||
return DriverStation.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* DriverStation constructor.
|
||||
*
|
||||
* <p>The single DriverStation instance is created statically with the instance static member
|
||||
* variable.
|
||||
*/
|
||||
private DriverStation() {
|
||||
m_joystickMutex = new Object();
|
||||
for (int i = 0; i < kJoystickPorts; i++) {
|
||||
m_joystickButtons[i] = new HALJoystickButtons();
|
||||
m_joystickAxes[i] = new HALJoystickAxes(HAL.kMaxJoystickAxes);
|
||||
m_joystickPOVs[i] = new HALJoystickPOVs(HAL.kMaxJoystickPOVs);
|
||||
|
||||
m_joystickButtonsCache[i] = new HALJoystickButtons();
|
||||
m_joystickAxesCache[i] = new HALJoystickAxes(HAL.kMaxJoystickAxes);
|
||||
m_joystickPOVsCache[i] = new HALJoystickPOVs(HAL.kMaxJoystickPOVs);
|
||||
}
|
||||
|
||||
m_controlWordMutex = new Object();
|
||||
m_controlWordCache = new ControlWord();
|
||||
m_lastControlWordUpdate = 0;
|
||||
|
||||
m_thread = new Thread(new DriverStationTask(this), "FRCDriverStation");
|
||||
m_thread.setPriority((Thread.NORM_PRIORITY + Thread.MAX_PRIORITY) / 2);
|
||||
|
||||
m_thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill the thread.
|
||||
*/
|
||||
public void release() {
|
||||
m_threadKeepAlive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report error to Driver Station. Also prints error to System.err Optionally appends Stack trace
|
||||
* to error message.
|
||||
*
|
||||
* @param printTrace If true, append stack trace to error string
|
||||
*/
|
||||
public static void reportError(String error, boolean printTrace) {
|
||||
reportErrorImpl(true, 1, error, printTrace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report warning to Driver Station. Also prints error to System.err Optionally appends Stack
|
||||
* trace to warning message.
|
||||
*
|
||||
* @param printTrace If true, append stack trace to warning string
|
||||
*/
|
||||
public static void reportWarning(String error, boolean printTrace) {
|
||||
reportErrorImpl(false, 1, error, printTrace);
|
||||
}
|
||||
|
||||
private static void reportErrorImpl(boolean isError, int code, String error, boolean
|
||||
printTrace) {
|
||||
StackTraceElement[] traces = Thread.currentThread().getStackTrace();
|
||||
String locString;
|
||||
if (traces.length > 3) {
|
||||
locString = traces[3].toString();
|
||||
} else {
|
||||
locString = "";
|
||||
}
|
||||
boolean haveLoc = false;
|
||||
String traceString = " at ";
|
||||
for (int i = 3; i < traces.length; i++) {
|
||||
String loc = traces[i].toString();
|
||||
traceString += loc + "\n";
|
||||
// get first user function
|
||||
if (!haveLoc && !loc.startsWith("edu.wpi.first.wpilibj")) {
|
||||
locString = loc;
|
||||
haveLoc = true;
|
||||
}
|
||||
}
|
||||
HAL.sendError(isError, code, false, error, locString, printTrace ? traceString : "", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the axis on a joystick. This depends on the mapping of the joystick connected
|
||||
* to the specified port.
|
||||
*
|
||||
* @param stick The joystick to read.
|
||||
* @param axis The analog axis value to read from the joystick.
|
||||
* @return The value of the axis on the joystick.
|
||||
*/
|
||||
public double getStickAxis(int stick, int axis) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
if (axis < 0 || axis >= HAL.kMaxJoystickAxes) {
|
||||
throw new RuntimeException("Joystick axis is out of range");
|
||||
}
|
||||
|
||||
boolean error = false;
|
||||
double retVal = 0.0;
|
||||
synchronized (m_joystickMutex) {
|
||||
if (axis >= m_joystickAxes[stick].m_count) {
|
||||
// set error
|
||||
error = true;
|
||||
retVal = 0.0;
|
||||
} else {
|
||||
retVal = m_joystickAxes[stick].m_axes[axis];
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
reportJoystickUnpluggedWarning("Joystick axis " + axis + " on port " + stick
|
||||
+ " not available, check if controller is plugged in");
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of a POV on the joystick.
|
||||
*
|
||||
* @return the angle of the POV in degrees, or -1 if the POV is not pressed.
|
||||
*/
|
||||
public int getStickPOV(int stick, int pov) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
if (pov < 0 || pov >= HAL.kMaxJoystickPOVs) {
|
||||
throw new RuntimeException("Joystick POV is out of range");
|
||||
}
|
||||
boolean error = false;
|
||||
int retVal = -1;
|
||||
synchronized (m_joystickMutex) {
|
||||
if (pov >= m_joystickPOVs[stick].m_count) {
|
||||
error = true;
|
||||
retVal = -1;
|
||||
} else {
|
||||
retVal = m_joystickPOVs[stick].m_povs[pov];
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
reportJoystickUnpluggedWarning("Joystick POV " + pov + " on port " + stick
|
||||
+ " not available, check if controller is plugged in");
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* The state of the buttons on the joystick.
|
||||
*
|
||||
* @param stick The joystick to read.
|
||||
* @return The state of the buttons on the joystick.
|
||||
*/
|
||||
public int getStickButtons(final int stick) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-3");
|
||||
}
|
||||
synchronized (m_joystickMutex) {
|
||||
return m_joystickButtons[stick].m_buttons;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The state of one joystick button. Button indexes begin at 1.
|
||||
*
|
||||
* @param stick The joystick to read.
|
||||
* @param button The button index, beginning at 1.
|
||||
* @return The state of the joystick button.
|
||||
*/
|
||||
public boolean getStickButton(final int stick, byte button) {
|
||||
if (button <= 0) {
|
||||
reportJoystickUnpluggedError("Button indexes begin at 1 in WPILib for C++ and Java\n");
|
||||
return false;
|
||||
}
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-3");
|
||||
}
|
||||
boolean error = false;
|
||||
boolean retVal = false;
|
||||
synchronized (m_joystickMutex) {
|
||||
if (button > m_joystickButtons[stick].m_count) {
|
||||
error = true;
|
||||
retVal = false;
|
||||
} else {
|
||||
retVal = ((0x1 << (button - 1)) & m_joystickButtons[stick].m_buttons) != 0;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
reportJoystickUnpluggedWarning("Joystick Button " + button + " on port " + stick
|
||||
+ " not available, check if controller is plugged in");
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of axes on a given joystick port.
|
||||
*
|
||||
* @param stick The joystick port number
|
||||
* @return The number of axes on the indicated joystick
|
||||
*/
|
||||
public int getStickAxisCount(int stick) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
synchronized (m_joystickMutex) {
|
||||
return m_joystickAxes[stick].m_count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of POVs on a given joystick port.
|
||||
*
|
||||
* @param stick The joystick port number
|
||||
* @return The number of POVs on the indicated joystick
|
||||
*/
|
||||
public int getStickPOVCount(int stick) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
synchronized (m_joystickMutex) {
|
||||
return m_joystickPOVs[stick].m_count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of buttons on a joystick.
|
||||
*
|
||||
* @param stick The joystick port number
|
||||
* @return The number of buttons on the indicated joystick
|
||||
*/
|
||||
public int getStickButtonCount(int stick) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
synchronized (m_joystickMutex) {
|
||||
return m_joystickButtons[stick].m_count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of isXbox on a joystick.
|
||||
*
|
||||
* @param stick The joystick port number
|
||||
* @return A boolean that returns the value of isXbox
|
||||
*/
|
||||
public boolean getJoystickIsXbox(int stick) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
boolean error = false;
|
||||
boolean retVal = false;
|
||||
synchronized (m_joystickMutex) {
|
||||
// TODO: Remove this when calling for descriptor on empty stick no longer
|
||||
// crashes
|
||||
if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].m_count) {
|
||||
error = true;
|
||||
retVal = false;
|
||||
} else if (HAL.getJoystickIsXbox((byte) stick) == 1) {
|
||||
retVal = true;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
reportJoystickUnpluggedWarning("Joystick on port " + stick
|
||||
+ " not available, check if controller is plugged in");
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of type on a joystick.
|
||||
*
|
||||
* @param stick The joystick port number
|
||||
* @return The value of type
|
||||
*/
|
||||
public int getJoystickType(int stick) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
boolean error = false;
|
||||
int retVal = -1;
|
||||
synchronized (m_joystickMutex) {
|
||||
// TODO: Remove this when calling for descriptor on empty stick no longer
|
||||
// crashes
|
||||
if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].m_count) {
|
||||
error = true;
|
||||
retVal = -1;
|
||||
} else {
|
||||
retVal = HAL.getJoystickType((byte) stick);
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
reportJoystickUnpluggedWarning("Joystick on port " + stick
|
||||
+ " not available, check if controller is plugged in");
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the joystick at a port.
|
||||
*
|
||||
* @param stick The joystick port number
|
||||
* @return The value of name
|
||||
*/
|
||||
public String getJoystickName(int stick) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
boolean error = false;
|
||||
String retVal = "";
|
||||
synchronized (m_joystickMutex) {
|
||||
// TODO: Remove this when calling for descriptor on empty stick no longer
|
||||
// crashes
|
||||
if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].m_count) {
|
||||
error = true;
|
||||
retVal = "";
|
||||
} else {
|
||||
retVal = HAL.getJoystickName((byte) stick);
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
reportJoystickUnpluggedWarning("Joystick on port " + stick
|
||||
+ " not available, check if controller is plugged in");
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the types of Axes on a given joystick port.
|
||||
*
|
||||
* @param stick The joystick port number
|
||||
* @param axis The target axis
|
||||
* @return What type of axis the axis is reporting to be
|
||||
*/
|
||||
public int getJoystickAxisType(int stick, int axis) {
|
||||
if (stick < 0 || stick >= kJoystickPorts) {
|
||||
throw new RuntimeException("Joystick index is out of range, should be 0-5");
|
||||
}
|
||||
|
||||
int retVal = -1;
|
||||
synchronized (m_joystickMutex) {
|
||||
retVal = HAL.getJoystickAxisType((byte) stick, (byte) axis);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the Driver Station requires the robot to be enabled.
|
||||
*
|
||||
* @return True if the robot is enabled, false otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
synchronized (m_controlWordMutex) {
|
||||
updateControlWord(false);
|
||||
return m_controlWordCache.getEnabled() && m_controlWordCache.getDSAttached();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the Driver Station requires the robot to be disabled.
|
||||
*
|
||||
* @return True if the robot should be disabled, false otherwise.
|
||||
*/
|
||||
public boolean isDisabled() {
|
||||
return !isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the Driver Station requires the robot to be running in
|
||||
* autonomous mode.
|
||||
*
|
||||
* @return True if autonomous mode should be enabled, false otherwise.
|
||||
*/
|
||||
public boolean isAutonomous() {
|
||||
synchronized (m_controlWordMutex) {
|
||||
updateControlWord(false);
|
||||
return m_controlWordCache.getAutonomous();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the Driver Station requires the robot to be running in
|
||||
* operator-controlled mode.
|
||||
*
|
||||
* @return True if operator-controlled mode should be enabled, false otherwise.
|
||||
*/
|
||||
public boolean isOperatorControl() {
|
||||
return !(isAutonomous() || isTest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the Driver Station requires the robot to be running in test
|
||||
* mode.
|
||||
*
|
||||
* @return True if test mode should be enabled, false otherwise.
|
||||
*/
|
||||
public boolean isTest() {
|
||||
synchronized (m_controlWordMutex) {
|
||||
updateControlWord(false);
|
||||
return m_controlWordCache.getTest();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the Driver Station is attached.
|
||||
*
|
||||
* @return True if Driver Station is attached, false otherwise.
|
||||
*/
|
||||
public boolean isDSAttached() {
|
||||
synchronized (m_controlWordMutex) {
|
||||
updateControlWord(false);
|
||||
return m_controlWordCache.getDSAttached();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if a new control packet from the driver station arrived since the last time this function
|
||||
* was called.
|
||||
*
|
||||
* @return True if the control data has been updated since the last call.
|
||||
*/
|
||||
public boolean isNewControlData() {
|
||||
return HAL.isNewControlData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the driver station attached to a Field Management System.
|
||||
*
|
||||
* @return true if the robot is competing on a field being controlled by a Field Management System
|
||||
*/
|
||||
public boolean isFMSAttached() {
|
||||
synchronized (m_controlWordMutex) {
|
||||
updateControlWord(false);
|
||||
return m_controlWordCache.getFMSAttached();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the FPGA outputs are enabled. The outputs may be disabled if
|
||||
* the robot is disabled or e-stopped, the watdhog has expired, or if the roboRIO browns out.
|
||||
*
|
||||
* @return True if the FPGA outputs are enabled.
|
||||
*/
|
||||
public boolean isSysActive() {
|
||||
return HAL.getSystemActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the system is browned out.
|
||||
*
|
||||
* @return True if the system is browned out
|
||||
*/
|
||||
public boolean isBrownedOut() {
|
||||
return HAL.getBrownedOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current alliance from the FMS.
|
||||
*
|
||||
* @return the current alliance
|
||||
*/
|
||||
public Alliance getAlliance() {
|
||||
AllianceStationID allianceStationID = HAL.getAllianceStation();
|
||||
if (allianceStationID == null) {
|
||||
return Alliance.Invalid;
|
||||
}
|
||||
|
||||
switch (allianceStationID) {
|
||||
case Red1:
|
||||
case Red2:
|
||||
case Red3:
|
||||
return Alliance.Red;
|
||||
|
||||
case Blue1:
|
||||
case Blue2:
|
||||
case Blue3:
|
||||
return Alliance.Blue;
|
||||
|
||||
default:
|
||||
return Alliance.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of the team's driver station controls.
|
||||
*
|
||||
* @return the location of the team's driver station controls: 1, 2, or 3
|
||||
*/
|
||||
public int getLocation() {
|
||||
AllianceStationID allianceStationID = HAL.getAllianceStation();
|
||||
if (allianceStationID == null) {
|
||||
return 0;
|
||||
}
|
||||
switch (allianceStationID) {
|
||||
case Red1:
|
||||
case Blue1:
|
||||
return 1;
|
||||
|
||||
case Red2:
|
||||
case Blue2:
|
||||
return 2;
|
||||
|
||||
case Blue3:
|
||||
case Red3:
|
||||
return 3;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for new data from the driver station.
|
||||
*/
|
||||
public void waitForData() {
|
||||
waitForData(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for new data or for timeout, which ever comes first. If timeout is 0, wait for new data
|
||||
* only.
|
||||
*
|
||||
* @param timeout The maximum time in seconds to wait.
|
||||
* @return true if there is new data, otherwise false
|
||||
*/
|
||||
public boolean waitForData(double timeout) {
|
||||
return HAL.waitForDSDataTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the approximate match time The FMS does not send an official match time to the robots,
|
||||
* but does send an approximate match time. The value will count down the time remaining in the
|
||||
* current period (auto or teleop). Warning: This is not an official time (so it cannot be used to
|
||||
* dispute ref calls or guarantee that a function will trigger before the match ends) The
|
||||
* Practice Match function of the DS approximates the behaviour seen on the field.
|
||||
*
|
||||
* @return Time remaining in current match period (auto or teleop) in seconds
|
||||
*/
|
||||
public double getMatchTime() {
|
||||
return HAL.getMatchTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the battery voltage.
|
||||
*
|
||||
* @return The battery voltage in Volts.
|
||||
*/
|
||||
public double getBatteryVoltage() {
|
||||
return PowerJNI.getVinVoltage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
||||
* purposes only.
|
||||
*
|
||||
* @param entering If true, starting disabled code; if false, leaving disabled code
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
public void InDisabled(boolean entering) {
|
||||
m_userInDisabled = entering;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
||||
* purposes only.
|
||||
*
|
||||
* @param entering If true, starting autonomous code; if false, leaving autonomous code
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
public void InAutonomous(boolean entering) {
|
||||
m_userInAutonomous = entering;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
||||
* purposes only.
|
||||
*
|
||||
* @param entering If true, starting teleop code; if false, leaving teleop code
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
public void InOperatorControl(boolean entering) {
|
||||
m_userInTeleop = entering;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
||||
* purposes only.
|
||||
*
|
||||
* @param entering If true, starting test code; if false, leaving test code
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
public void InTest(boolean entering) {
|
||||
m_userInTest = entering;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from the DS task for the user. If no new data exists, it will just be returned,
|
||||
* otherwise the data will be copied from the DS polling loop.
|
||||
*/
|
||||
protected void getData() {
|
||||
// Get the status of all of the joysticks
|
||||
for (byte stick = 0; stick < kJoystickPorts; stick++) {
|
||||
m_joystickAxesCache[stick].m_count =
|
||||
HAL.getJoystickAxes(stick, m_joystickAxesCache[stick].m_axes);
|
||||
m_joystickPOVsCache[stick].m_count =
|
||||
HAL.getJoystickPOVs(stick, m_joystickPOVsCache[stick].m_povs);
|
||||
m_joystickButtonsCache[stick].m_buttons = HAL.getJoystickButtons(stick, m_buttonCountBuffer);
|
||||
m_joystickButtonsCache[stick].m_count = m_buttonCountBuffer.get(0);
|
||||
}
|
||||
// Force a control word update, to make sure the data is the newest.
|
||||
updateControlWord(true);
|
||||
|
||||
// lock joystick mutex to swap cache data
|
||||
synchronized (m_joystickMutex) {
|
||||
// move cache to actual data
|
||||
HALJoystickAxes[] currentAxes = m_joystickAxes;
|
||||
m_joystickAxes = m_joystickAxesCache;
|
||||
m_joystickAxesCache = currentAxes;
|
||||
|
||||
HALJoystickButtons[] currentButtons = m_joystickButtons;
|
||||
m_joystickButtons = m_joystickButtonsCache;
|
||||
m_joystickButtonsCache = currentButtons;
|
||||
|
||||
HALJoystickPOVs[] currentPOVs = m_joystickPOVs;
|
||||
m_joystickPOVs = m_joystickPOVsCache;
|
||||
m_joystickPOVsCache = currentPOVs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports errors related to unplugged joysticks Throttles the errors so that they don't overwhelm
|
||||
* the DS.
|
||||
*/
|
||||
private void reportJoystickUnpluggedError(String message) {
|
||||
double currentTime = Timer.getFPGATimestamp();
|
||||
if (currentTime > m_nextMessageTime) {
|
||||
reportError(message, false);
|
||||
m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports errors related to unplugged joysticks Throttles the errors so that they don't overwhelm
|
||||
* the DS.
|
||||
*/
|
||||
private void reportJoystickUnpluggedWarning(String message) {
|
||||
double currentTime = Timer.getFPGATimestamp();
|
||||
if (currentTime > m_nextMessageTime) {
|
||||
reportWarning(message, false);
|
||||
m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the service routine for the DS polling m_thread.
|
||||
*/
|
||||
private void run() {
|
||||
int safetyCounter = 0;
|
||||
while (m_threadKeepAlive) {
|
||||
HAL.waitForDSData();
|
||||
getData();
|
||||
|
||||
if (++safetyCounter >= 4) {
|
||||
MotorSafetyHelper.checkMotors();
|
||||
safetyCounter = 0;
|
||||
}
|
||||
if (m_userInDisabled) {
|
||||
HAL.observeUserProgramDisabled();
|
||||
}
|
||||
if (m_userInAutonomous) {
|
||||
HAL.observeUserProgramAutonomous();
|
||||
}
|
||||
if (m_userInTeleop) {
|
||||
HAL.observeUserProgramTeleop();
|
||||
}
|
||||
if (m_userInTest) {
|
||||
HAL.observeUserProgramTest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the data in the control word cache. Updates if the force parameter is set, or if
|
||||
* 50ms have passed since the last update.
|
||||
*
|
||||
* @param force True to force an update to the cache, otherwise update if 50ms have passed.
|
||||
*/
|
||||
private void updateControlWord(boolean force) {
|
||||
long now = System.currentTimeMillis();
|
||||
synchronized (m_controlWordMutex) {
|
||||
if (now - m_lastControlWordUpdate > 50 || force) {
|
||||
HAL.getControlWord(m_controlWordCache);
|
||||
m_lastControlWordUpdate = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
608
wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java
Normal file
608
wpilibj/src/main/java/edu/wpi/first/wpilibj/Encoder.java
Normal file
@@ -0,0 +1,608 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.EncoderJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.util.AllocationException;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Class to read quadrature encoders.
|
||||
*
|
||||
* <p>Quadrature encoders are devices that count shaft rotation and can sense direction. The output
|
||||
* of the Encoder class is an integer that can count either up or down, and can go negative for
|
||||
* reverse direction counting. When creating Encoders, a direction can be supplied that inverts the
|
||||
* sense of the output to make code more readable if the encoder is mounted such that forward
|
||||
* movement generates negative values. Quadrature encoders have two digital outputs, an A Channel
|
||||
* and a B Channel, that are out of phase with each other for direction sensing.
|
||||
*
|
||||
* <p>All encoders will immediately start counting - reset() them if you need them to be zeroed
|
||||
* before use.
|
||||
*/
|
||||
public class Encoder extends SensorBase implements CounterBase, PIDSource, LiveWindowSendable {
|
||||
|
||||
public enum IndexingType {
|
||||
kResetWhileHigh(0), kResetWhileLow(1), kResetOnFallingEdge(2), kResetOnRisingEdge(3);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
IndexingType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The a source.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
protected DigitalSource m_aSource; // the A phase of the quad encoder
|
||||
/**
|
||||
* The b source.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
protected DigitalSource m_bSource; // the B phase of the quad encoder
|
||||
/**
|
||||
* The index source.
|
||||
*/
|
||||
protected DigitalSource m_indexSource = null; // Index on some encoders
|
||||
private boolean m_allocatedA;
|
||||
private boolean m_allocatedB;
|
||||
private boolean m_allocatedI;
|
||||
private PIDSourceType m_pidSource;
|
||||
|
||||
private int m_encoder; // the HAL encoder object
|
||||
|
||||
|
||||
/**
|
||||
* Common initialization code for Encoders. This code allocates resources for Encoders and is
|
||||
* common to all constructors.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param reverseDirection If true, counts down instead of up (this is all relative)
|
||||
*/
|
||||
private void initEncoder(boolean reverseDirection, final EncodingType type) {
|
||||
m_encoder = EncoderJNI.initializeEncoder(m_aSource.getPortHandleForRouting(),
|
||||
m_aSource.getAnalogTriggerTypeForRouting(), m_bSource.getPortHandleForRouting(),
|
||||
m_bSource.getAnalogTriggerTypeForRouting(), reverseDirection, type.value);
|
||||
|
||||
m_pidSource = PIDSourceType.kDisplacement;
|
||||
|
||||
HAL.report(tResourceType.kResourceType_Encoder, getFPGAIndex(), type.value);
|
||||
LiveWindow.addSensor("Encoder", m_aSource.getChannel(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a and b channels.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param channelA The a channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port
|
||||
* @param channelB The b channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port
|
||||
* @param reverseDirection represents the orientation of the encoder and inverts the output values
|
||||
* if necessary so forward represents positive values.
|
||||
*/
|
||||
public Encoder(final int channelA, final int channelB, boolean reverseDirection) {
|
||||
m_allocatedA = true;
|
||||
m_allocatedB = true;
|
||||
m_allocatedI = false;
|
||||
m_aSource = new DigitalInput(channelA);
|
||||
m_bSource = new DigitalInput(channelB);
|
||||
initEncoder(reverseDirection, EncodingType.k4X);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a and b channels.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param channelA The a channel digital input channel.
|
||||
* @param channelB The b channel digital input channel.
|
||||
*/
|
||||
public Encoder(final int channelA, final int channelB) {
|
||||
this(channelA, channelB, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a and b channels.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param channelA The a channel digital input channel.
|
||||
* @param channelB The b channel digital input channel.
|
||||
* @param reverseDirection represents the orientation of the encoder and inverts the output values
|
||||
* if necessary so forward represents positive values.
|
||||
* @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
|
||||
* selected, then an encoder FPGA object is used and the returned counts
|
||||
* will be 4x the encoder spec'd value since all rising and falling edges
|
||||
* are counted. If 1X or 2X are selected then a m_counter object will be
|
||||
* used and the returned value will either exactly match the spec'd count
|
||||
* or be double (2x) the spec'd count.
|
||||
*/
|
||||
public Encoder(final int channelA, final int channelB, boolean reverseDirection,
|
||||
final EncodingType encodingType) {
|
||||
requireNonNull(encodingType, "Given encoding type was null");
|
||||
|
||||
m_allocatedA = true;
|
||||
m_allocatedB = true;
|
||||
m_allocatedI = false;
|
||||
m_aSource = new DigitalInput(channelA);
|
||||
m_bSource = new DigitalInput(channelB);
|
||||
initEncoder(reverseDirection, encodingType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a and b channels. Using an index pulse forces 4x
|
||||
* encoding
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param channelA The a channel digital input channel.
|
||||
* @param channelB The b channel digital input channel.
|
||||
* @param indexChannel The index channel digital input channel.
|
||||
* @param reverseDirection represents the orientation of the encoder and inverts the output values
|
||||
* if necessary so forward represents positive values.
|
||||
*/
|
||||
public Encoder(final int channelA, final int channelB, final int indexChannel,
|
||||
boolean reverseDirection) {
|
||||
m_allocatedA = true;
|
||||
m_allocatedB = true;
|
||||
m_allocatedI = true;
|
||||
m_aSource = new DigitalInput(channelA);
|
||||
m_bSource = new DigitalInput(channelB);
|
||||
m_indexSource = new DigitalInput(indexChannel);
|
||||
initEncoder(reverseDirection, EncodingType.k4X);
|
||||
setIndexSource(m_indexSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a and b channels. Using an index pulse forces 4x
|
||||
* encoding
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param channelA The a channel digital input channel.
|
||||
* @param channelB The b channel digital input channel.
|
||||
* @param indexChannel The index channel digital input channel.
|
||||
*/
|
||||
public Encoder(final int channelA, final int channelB, final int indexChannel) {
|
||||
this(channelA, channelB, indexChannel, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used
|
||||
* in the case where the digital inputs are shared. The Encoder class will not allocate the
|
||||
* digital inputs and assume that they already are counted.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param sourceA The source that should be used for the a channel.
|
||||
* @param sourceB the source that should be used for the b channel.
|
||||
* @param reverseDirection represents the orientation of the encoder and inverts the output values
|
||||
* if necessary so forward represents positive values.
|
||||
*/
|
||||
public Encoder(DigitalSource sourceA, DigitalSource sourceB, boolean reverseDirection) {
|
||||
requireNonNull(sourceA, "Digital Source A was null");
|
||||
requireNonNull(sourceB, "Digital Source B was null");
|
||||
|
||||
m_allocatedA = false;
|
||||
m_allocatedB = false;
|
||||
m_allocatedI = false;
|
||||
m_aSource = sourceA;
|
||||
m_bSource = sourceB;
|
||||
initEncoder(reverseDirection, EncodingType.k4X);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used
|
||||
* in the case where the digital inputs are shared. The Encoder class will not allocate the
|
||||
* digital inputs and assume that they already are counted.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param sourceA The source that should be used for the a channel.
|
||||
* @param sourceB the source that should be used for the b channel.
|
||||
*/
|
||||
public Encoder(DigitalSource sourceA, DigitalSource sourceB) {
|
||||
this(sourceA, sourceB, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used
|
||||
* in the case where the digital inputs are shared. The Encoder class will not allocate the
|
||||
* digital inputs and assume that they already are counted.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param sourceA The source that should be used for the a channel.
|
||||
* @param sourceB the source that should be used for the b channel.
|
||||
* @param reverseDirection represents the orientation of the encoder and inverts the output values
|
||||
* if necessary so forward represents positive values.
|
||||
* @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
|
||||
* selected, then an encoder FPGA object is used and the returned counts
|
||||
* will be 4x the encoder spec'd value since all rising and falling edges
|
||||
* are counted. If 1X or 2X are selected then a m_counter object will be
|
||||
* used and the returned value will either exactly match the spec'd count
|
||||
* or be double (2x) the spec'd count.
|
||||
*/
|
||||
public Encoder(DigitalSource sourceA, DigitalSource sourceB, boolean reverseDirection,
|
||||
final EncodingType encodingType) {
|
||||
requireNonNull(sourceA, "Digital Source A was null");
|
||||
requireNonNull(sourceB, "Digital Source B was null");
|
||||
requireNonNull(encodingType, "Given encoding type was null");
|
||||
|
||||
m_allocatedA = false;
|
||||
m_allocatedB = false;
|
||||
m_allocatedI = false;
|
||||
m_aSource = sourceA;
|
||||
m_bSource = sourceB;
|
||||
initEncoder(reverseDirection, encodingType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a, b and index channels as digital inputs. This
|
||||
* is used in the case where the digital inputs are shared. The Encoder class will not allocate
|
||||
* the digital inputs and assume that they already are counted.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param sourceA The source that should be used for the a channel.
|
||||
* @param sourceB the source that should be used for the b channel.
|
||||
* @param indexSource the source that should be used for the index channel.
|
||||
* @param reverseDirection represents the orientation of the encoder and inverts the output values
|
||||
* if necessary so forward represents positive values.
|
||||
*/
|
||||
public Encoder(DigitalSource sourceA, DigitalSource sourceB, DigitalSource indexSource,
|
||||
boolean reverseDirection) {
|
||||
requireNonNull(sourceA, "Digital Source A was null");
|
||||
requireNonNull(sourceB, "Digital Source B was null");
|
||||
|
||||
m_allocatedA = false;
|
||||
m_allocatedB = false;
|
||||
m_allocatedI = false;
|
||||
m_aSource = sourceA;
|
||||
m_bSource = sourceB;
|
||||
m_indexSource = indexSource;
|
||||
initEncoder(reverseDirection, EncodingType.k4X);
|
||||
setIndexSource(indexSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoder constructor. Construct a Encoder given a, b and index channels as digital inputs. This
|
||||
* is used in the case where the digital inputs are shared. The Encoder class will not allocate
|
||||
* the digital inputs and assume that they already are counted.
|
||||
*
|
||||
* <p>The encoder will start counting immediately.
|
||||
*
|
||||
* @param sourceA The source that should be used for the a channel.
|
||||
* @param sourceB the source that should be used for the b channel.
|
||||
* @param indexSource the source that should be used for the index channel.
|
||||
*/
|
||||
public Encoder(DigitalSource sourceA, DigitalSource sourceB, DigitalSource indexSource) {
|
||||
this(sourceA, sourceB, indexSource, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the FPGA index of the encoder.
|
||||
*
|
||||
* @return The Encoder's FPGA index.
|
||||
*/
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public int getFPGAIndex() {
|
||||
return EncoderJNI.getEncoderFPGAIndex(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to divide raw edge counts down to spec'd counts.
|
||||
*
|
||||
* @return The encoding scale factor 1x, 2x, or 4x, per the requested encoding type.
|
||||
*/
|
||||
public int getEncodingScale() {
|
||||
return EncoderJNI.getEncoderEncodingScale(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the resources used by this object.
|
||||
*/
|
||||
public void free() {
|
||||
if (m_aSource != null && m_allocatedA) {
|
||||
m_aSource.free();
|
||||
m_allocatedA = false;
|
||||
}
|
||||
if (m_bSource != null && m_allocatedB) {
|
||||
m_bSource.free();
|
||||
m_allocatedB = false;
|
||||
}
|
||||
if (m_indexSource != null && m_allocatedI) {
|
||||
m_indexSource.free();
|
||||
m_allocatedI = false;
|
||||
}
|
||||
|
||||
m_aSource = null;
|
||||
m_bSource = null;
|
||||
m_indexSource = null;
|
||||
EncoderJNI.freeEncoder(m_encoder);
|
||||
m_encoder = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw value from the encoder. The raw value is the actual count unscaled by the 1x, 2x,
|
||||
* or 4x scale factor.
|
||||
*
|
||||
* @return Current raw count from the encoder
|
||||
*/
|
||||
public int getRaw() {
|
||||
return EncoderJNI.getEncoderRaw(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current count. Returns the current count on the Encoder. This method compensates for
|
||||
* the decoding type.
|
||||
*
|
||||
* @return Current count from the Encoder adjusted for the 1x, 2x, or 4x scale factor.
|
||||
*/
|
||||
public int get() {
|
||||
return EncoderJNI.getEncoder(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the Encoder distance to zero. Resets the current count to zero on the encoder.
|
||||
*/
|
||||
public void reset() {
|
||||
EncoderJNI.resetEncoder(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the period of the most recent pulse. Returns the period of the most recent Encoder
|
||||
* pulse in seconds. This method compensates for the decoding type.
|
||||
*
|
||||
* <p><b>Warning:</b> This returns unscaled periods. Use getRate() for rates that are scaled using
|
||||
* the value from setDistancePerPulse().
|
||||
*
|
||||
* @return Period in seconds of the most recent pulse.
|
||||
* @deprecated Use getRate() in favor of this method.
|
||||
*/
|
||||
@Deprecated
|
||||
public double getPeriod() {
|
||||
return EncoderJNI.getEncoderPeriod(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum period for stopped detection. Sets the value that represents the maximum
|
||||
* period of the Encoder before it will assume that the attached device is stopped. This timeout
|
||||
* allows users to determine if the wheels or other shaft has stopped rotating. This method
|
||||
* compensates for the decoding type.
|
||||
*
|
||||
* @param maxPeriod The maximum time between rising and falling edges before the FPGA will report
|
||||
* the device stopped. This is expressed in seconds.
|
||||
*/
|
||||
public void setMaxPeriod(double maxPeriod) {
|
||||
EncoderJNI.setEncoderMaxPeriod(m_encoder, maxPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the encoder is stopped. Using the MaxPeriod value, a boolean is returned that is
|
||||
* true if the encoder is considered stopped and false if it is still moving. A stopped encoder is
|
||||
* one where the most recent pulse width exceeds the MaxPeriod.
|
||||
*
|
||||
* @return True if the encoder is considered stopped.
|
||||
*/
|
||||
public boolean getStopped() {
|
||||
return EncoderJNI.getEncoderStopped(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* The last direction the encoder value changed.
|
||||
*
|
||||
* @return The last direction the encoder value changed.
|
||||
*/
|
||||
public boolean getDirection() {
|
||||
return EncoderJNI.getEncoderDirection(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distance the robot has driven since the last reset as scaled by the value from {@link
|
||||
* #setDistancePerPulse(double)}.
|
||||
*
|
||||
* @return The distance driven since the last reset
|
||||
*/
|
||||
public double getDistance() {
|
||||
return EncoderJNI.getEncoderDistance(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current rate of the encoder. Units are distance per second as scaled by the value from
|
||||
* setDistancePerPulse().
|
||||
*
|
||||
* @return The current rate of the encoder.
|
||||
*/
|
||||
public double getRate() {
|
||||
return EncoderJNI.getEncoderRate(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum rate of the device before the hardware reports it stopped.
|
||||
*
|
||||
* @param minRate The minimum rate. The units are in distance per second as scaled by the value
|
||||
* from setDistancePerPulse().
|
||||
*/
|
||||
public void setMinRate(double minRate) {
|
||||
EncoderJNI.setEncoderMinRate(m_encoder, minRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the distance per pulse for this encoder. This sets the multiplier used to determine the
|
||||
* distance driven based on the count value from the encoder. Do not include the decoding type in
|
||||
* this scale. The library already compensates for the decoding type. Set this value based on the
|
||||
* encoder's rated Pulses per Revolution and factor in gearing reductions following the encoder
|
||||
* shaft. This distance can be in any units you like, linear or angular.
|
||||
*
|
||||
* @param distancePerPulse The scale factor that will be used to convert pulses to useful units.
|
||||
*/
|
||||
public void setDistancePerPulse(double distancePerPulse) {
|
||||
EncoderJNI.setEncoderDistancePerPulse(m_encoder, distancePerPulse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the direction sensing for this encoder. This sets the direction sensing on the encoder so
|
||||
* that it could count in the correct software direction regardless of the mounting.
|
||||
*
|
||||
* @param reverseDirection true if the encoder direction should be reversed
|
||||
*/
|
||||
public void setReverseDirection(boolean reverseDirection) {
|
||||
EncoderJNI.setEncoderReverseDirection(m_encoder, reverseDirection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Samples to Average which specifies the number of samples of the timer to average when
|
||||
* calculating the period. Perform averaging to account for mechanical imperfections or as
|
||||
* oversampling to increase resolution.
|
||||
*
|
||||
* @param samplesToAverage The number of samples to average from 1 to 127.
|
||||
*/
|
||||
public void setSamplesToAverage(int samplesToAverage) {
|
||||
EncoderJNI.setEncoderSamplesToAverage(m_encoder, samplesToAverage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Samples to Average which specifies the number of samples of the timer to average when
|
||||
* calculating the period. Perform averaging to account for mechanical imperfections or as
|
||||
* oversampling to increase resolution.
|
||||
*
|
||||
* @return SamplesToAverage The number of samples being averaged (from 1 to 127)
|
||||
*/
|
||||
public int getSamplesToAverage() {
|
||||
return EncoderJNI.getEncoderSamplesToAverage(m_encoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which parameter of the encoder you are using as a process control variable. The encoder
|
||||
* class supports the rate and distance parameters.
|
||||
*
|
||||
* @param pidSource An enum to select the parameter.
|
||||
*/
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
m_pidSource = pidSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return m_pidSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the PIDSource interface.
|
||||
*
|
||||
* @return The current value of the selected source parameter.
|
||||
*/
|
||||
public double pidGet() {
|
||||
switch (m_pidSource) {
|
||||
case kDisplacement:
|
||||
return getDistance();
|
||||
case kRate:
|
||||
return getRate();
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index source for the encoder. When this source is activated, the encoder count
|
||||
* automatically resets.
|
||||
*
|
||||
* @param channel A DIO channel to set as the encoder index
|
||||
*/
|
||||
public void setIndexSource(int channel) {
|
||||
setIndexSource(channel, IndexingType.kResetOnRisingEdge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index source for the encoder. When this source is activated, the encoder count
|
||||
* automatically resets.
|
||||
*
|
||||
* @param source A digital source to set as the encoder index
|
||||
*/
|
||||
public void setIndexSource(DigitalSource source) {
|
||||
setIndexSource(source, IndexingType.kResetOnRisingEdge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index source for the encoder. When this source rises, the encoder count automatically
|
||||
* resets.
|
||||
*
|
||||
* @param channel A DIO channel to set as the encoder index
|
||||
* @param type The state that will cause the encoder to reset
|
||||
*/
|
||||
public void setIndexSource(int channel, IndexingType type) {
|
||||
if (m_allocatedI) {
|
||||
throw new AllocationException("Digital Input for Indexing already allocated");
|
||||
}
|
||||
m_indexSource = new DigitalInput(channel);
|
||||
m_allocatedI = true;
|
||||
setIndexSource(m_indexSource, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index source for the encoder. When this source rises, the encoder count automatically
|
||||
* resets.
|
||||
*
|
||||
* @param source A digital source to set as the encoder index
|
||||
* @param type The state that will cause the encoder to reset
|
||||
*/
|
||||
public void setIndexSource(DigitalSource source, IndexingType type) {
|
||||
EncoderJNI.setEncoderIndexSource(m_encoder, source.getPortHandleForRouting(),
|
||||
source.getAnalogTriggerTypeForRouting(), type.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
public String getSmartDashboardType() {
|
||||
if (EncoderJNI.getEncoderEncodingType(m_encoder) == EncodingType.k4X.value) {
|
||||
return "Quadrature Encoder";
|
||||
}
|
||||
return "Encoder";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Speed", getRate());
|
||||
m_table.putNumber("Distance", getDistance());
|
||||
m_table.putNumber("Distance per Tick", EncoderJNI.getEncoderDistancePerPulse(m_encoder));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
58
wpilibj/src/main/java/edu/wpi/first/wpilibj/GamepadBase.java
Normal file
58
wpilibj/src/main/java/edu/wpi/first/wpilibj/GamepadBase.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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;
|
||||
|
||||
/**
|
||||
* Gamepad Interface.
|
||||
*/
|
||||
public abstract class GamepadBase extends GenericHID {
|
||||
public GamepadBase(int port) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
public abstract double getRawAxis(int axis);
|
||||
|
||||
/**
|
||||
* Is the bumper pressed.
|
||||
*
|
||||
* @param hand which hand
|
||||
* @return true if the bumper is pressed
|
||||
*/
|
||||
public abstract boolean getBumper(Hand hand);
|
||||
|
||||
/**
|
||||
* Is the bumper pressed.
|
||||
*
|
||||
* @return true if the bumper is pressed
|
||||
*/
|
||||
public boolean getBumper() {
|
||||
return getBumper(Hand.kRight);
|
||||
}
|
||||
|
||||
public abstract boolean getStickButton(Hand hand);
|
||||
|
||||
public boolean getStickButton() {
|
||||
return getStickButton(Hand.kRight);
|
||||
}
|
||||
|
||||
public abstract boolean getRawButton(int button);
|
||||
|
||||
public abstract int getPOV(int pov);
|
||||
|
||||
public abstract int getPOVCount();
|
||||
|
||||
public abstract HIDType getType();
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public abstract void setOutput(int outputNumber, boolean value);
|
||||
|
||||
public abstract void setOutputs(int value);
|
||||
|
||||
public abstract void setRumble(RumbleType type, double value);
|
||||
}
|
||||
93
wpilibj/src/main/java/edu/wpi/first/wpilibj/GearTooth.java
Normal file
93
wpilibj/src/main/java/edu/wpi/first/wpilibj/GearTooth.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* Alias for counter class. Implement the gear tooth sensor supplied by FIRST. Currently there is no
|
||||
* reverse sensing on the gear tooth sensor, but in future versions we might implement the necessary
|
||||
* timing in the FPGA to sense direction.
|
||||
*/
|
||||
public class GearTooth extends Counter {
|
||||
|
||||
private static final double kGearToothThreshold = 55e-6;
|
||||
|
||||
/**
|
||||
* Common code called by the constructors.
|
||||
*/
|
||||
public void enableDirectionSensing(boolean directionSensitive) {
|
||||
if (directionSensitive) {
|
||||
setPulseLengthMode(kGearToothThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a GearTooth sensor given a channel.
|
||||
*
|
||||
* <p>No direction sensing is assumed.
|
||||
*
|
||||
* @param channel The GPIO channel that the sensor is connected to.
|
||||
*/
|
||||
public GearTooth(final int channel) {
|
||||
this(channel, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a GearTooth sensor given a channel.
|
||||
*
|
||||
* @param channel The DIO channel that the sensor is connected to. 0-9 are on-board,
|
||||
* 10-25 are on the MXP port
|
||||
* @param directionSensitive True to enable the pulse length decoding in hardware to specify count
|
||||
* direction.
|
||||
*/
|
||||
public GearTooth(final int channel, boolean directionSensitive) {
|
||||
super(channel);
|
||||
enableDirectionSensing(directionSensitive);
|
||||
if (directionSensitive) {
|
||||
HAL.report(tResourceType.kResourceType_GearTooth, channel, 0, "D");
|
||||
} else {
|
||||
HAL.report(tResourceType.kResourceType_GearTooth, channel, 0);
|
||||
}
|
||||
LiveWindow.addSensor("GearTooth", channel, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a GearTooth sensor given a digital input. This should be used when sharing digital
|
||||
* inputs.
|
||||
*
|
||||
* @param source An existing DigitalSource object (such as a DigitalInput)
|
||||
* @param directionSensitive True to enable the pulse length decoding in hardware to specify count
|
||||
* direction.
|
||||
*/
|
||||
public GearTooth(DigitalSource source, boolean directionSensitive) {
|
||||
super(source);
|
||||
enableDirectionSensing(directionSensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a GearTooth sensor given a digital input. This should be used when sharing digial
|
||||
* inputs.
|
||||
*
|
||||
* <p>No direction sensing is assumed.
|
||||
*
|
||||
* @param source An object that fully descibes the input that the sensor is connected to.
|
||||
*/
|
||||
public GearTooth(DigitalSource source) {
|
||||
this(source, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
public String getSmartDashboardType() {
|
||||
return "Gear Tooth";
|
||||
}
|
||||
}
|
||||
184
wpilibj/src/main/java/edu/wpi/first/wpilibj/GenericHID.java
Normal file
184
wpilibj/src/main/java/edu/wpi/first/wpilibj/GenericHID.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* GenericHID Interface.
|
||||
*/
|
||||
public abstract class GenericHID {
|
||||
/**
|
||||
* Represents a rumble output on the JoyStick.
|
||||
*/
|
||||
public enum RumbleType {
|
||||
kLeftRumble, kRightRumble
|
||||
}
|
||||
|
||||
public enum HIDType {
|
||||
kUnknown(-1),
|
||||
kXInputUnknown(0),
|
||||
kXInputGamepad(1),
|
||||
kXInputWheel(2),
|
||||
kXInputArcadeStick(3),
|
||||
kXInputFlightStick(4),
|
||||
kXInputDancePad(5),
|
||||
kXInputGuitar(6),
|
||||
kXInputGuitar2(7),
|
||||
kXInputDrumKit(8),
|
||||
kXInputGuitar3(11),
|
||||
kXInputArcadePad(19),
|
||||
kHIDJoystick(20),
|
||||
kHIDGamepad(21),
|
||||
kHIDDriving(22),
|
||||
kHIDFlight(23),
|
||||
kHID1stPerson(24);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private HIDType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Which hand the Human Interface Device is associated with.
|
||||
*/
|
||||
public enum Hand {
|
||||
kLeft(0), kRight(1);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private Hand(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private final int m_port;
|
||||
|
||||
public GenericHID(int port) {
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x position of the HID.
|
||||
*
|
||||
* @return the x position of the HID
|
||||
*/
|
||||
public final double getX() {
|
||||
return getX(Hand.kRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the x position of HID.
|
||||
*
|
||||
* @param hand which hand, left or right
|
||||
* @return the x position
|
||||
*/
|
||||
public abstract double getX(Hand hand);
|
||||
|
||||
/**
|
||||
* Get the y position of the HID.
|
||||
*
|
||||
* @return the y position
|
||||
*/
|
||||
public final double getY() {
|
||||
return getY(Hand.kRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the y position of the HID.
|
||||
*
|
||||
* @param hand which hand, left or right
|
||||
* @return the y position
|
||||
*/
|
||||
public abstract double getY(Hand hand);
|
||||
|
||||
/**
|
||||
* Get the raw axis.
|
||||
*
|
||||
* @param which index of the axis
|
||||
* @return the raw value of the selected axis
|
||||
*/
|
||||
public abstract double getRawAxis(int which);
|
||||
|
||||
/**
|
||||
* Is the given button pressed.
|
||||
*
|
||||
* @param button which button number
|
||||
* @return true if the button is pressed
|
||||
*/
|
||||
public abstract boolean getRawButton(int button);
|
||||
|
||||
/**
|
||||
* Get the angle in degrees of a POV on the HID.
|
||||
*
|
||||
* <p>The POV angles start at 0 in the up direction, and increase clockwise (eg right is 90,
|
||||
* upper-left is 315).
|
||||
*
|
||||
* @param pov The index of the POV to read (starting at 0)
|
||||
* @return the angle of the POV in degrees, or -1 if the POV is not pressed.
|
||||
*/
|
||||
public abstract int getPOV(int pov);
|
||||
|
||||
public int getPOV() {
|
||||
return getPOV(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the current HID, return the number of POVs.
|
||||
*/
|
||||
public abstract int getPOVCount();
|
||||
|
||||
/**
|
||||
* Get the port number of the HID.
|
||||
*
|
||||
* @return The port number of the HID.
|
||||
*/
|
||||
public int getPort() {
|
||||
return m_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the HID.
|
||||
*
|
||||
* @return the type of the HID.
|
||||
*/
|
||||
public abstract HIDType getType();
|
||||
|
||||
/**
|
||||
* Get the name of the HID.
|
||||
*
|
||||
* @return the name of the HID.
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
/**
|
||||
* Set a single HID output value for the HID.
|
||||
*
|
||||
* @param outputNumber The index of the output to set (1-32)
|
||||
* @param value The value to set the output to
|
||||
*/
|
||||
public abstract void setOutput(int outputNumber, boolean value);
|
||||
|
||||
/**
|
||||
* Set all HID output values for the HID.
|
||||
*
|
||||
* @param value The 32 bit output value (1 bit for each output)
|
||||
*/
|
||||
public abstract void setOutputs(int value);
|
||||
|
||||
/**
|
||||
* Set the rumble output for the HID. The DS currently supports 2 rumble values, left rumble and
|
||||
* right rumble.
|
||||
*
|
||||
* @param type Which rumble value to set
|
||||
* @param value The normalized value (0 to 1) to set the rumble to
|
||||
*/
|
||||
public abstract void setRumble(RumbleType type, double value);
|
||||
}
|
||||
90
wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java
Normal file
90
wpilibj/src/main/java/edu/wpi/first/wpilibj/GyroBase.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.interfaces.Gyro;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* GyroBase is the common base class for Gyro implementations such as AnalogGyro.
|
||||
*/
|
||||
public abstract class GyroBase extends SensorBase implements Gyro, PIDSource, LiveWindowSendable {
|
||||
private PIDSourceType m_pidSource = PIDSourceType.kDisplacement;
|
||||
|
||||
/**
|
||||
* Set which parameter of the gyro you are using as a process control variable. The Gyro class
|
||||
* supports the rate and displacement parameters
|
||||
*
|
||||
* @param pidSource An enum to select the parameter.
|
||||
*/
|
||||
@Override
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
m_pidSource = pidSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return m_pidSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output of the gyro for use with PIDControllers. May be the angle or rate depending on
|
||||
* the set PIDSourceType
|
||||
*
|
||||
* @return the output according to the gyro
|
||||
*/
|
||||
@Override
|
||||
public double pidGet() {
|
||||
switch (m_pidSource) {
|
||||
case kRate:
|
||||
return getRate();
|
||||
case kDisplacement:
|
||||
return getAngle();
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Gyro";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", getAngle());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.util.BaseSystemNotInitializedException;
|
||||
|
||||
/**
|
||||
* Support for high level usage reporting.
|
||||
*/
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
public class HLUsageReporting {
|
||||
private static Interface impl;
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
public static void SetImplementation(Interface implementation) {
|
||||
impl = implementation;
|
||||
}
|
||||
|
||||
public static void reportScheduler() {
|
||||
if (impl != null) {
|
||||
impl.reportScheduler();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(Interface.class, HLUsageReporting.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static void reportPIDController(int num) {
|
||||
if (impl != null) {
|
||||
impl.reportPIDController(num);
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(Interface.class, HLUsageReporting.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static void reportSmartDashboard() {
|
||||
if (impl != null) {
|
||||
impl.reportSmartDashboard();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(Interface.class, HLUsageReporting.class);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Interface {
|
||||
void reportScheduler();
|
||||
|
||||
void reportPIDController(int num);
|
||||
|
||||
void reportSmartDashboard();
|
||||
}
|
||||
|
||||
public static class Null implements Interface {
|
||||
public void reportScheduler() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public void reportPIDController(int num) {
|
||||
}
|
||||
|
||||
public void reportSmartDashboard() {
|
||||
}
|
||||
}
|
||||
}
|
||||
346
wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java
Normal file
346
wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java
Normal file
@@ -0,0 +1,346 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.nio.ByteBuffer;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.hal.I2CJNI;
|
||||
import edu.wpi.first.wpilibj.util.BoundaryException;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* I2C bus interface class.
|
||||
*
|
||||
* <p>This class is intended to be used by sensor (and other I2C device) drivers. It probably should
|
||||
* not be used directly.
|
||||
*/
|
||||
public class I2C extends SensorBase {
|
||||
public enum Port {
|
||||
kOnboard(0), kMXP(1);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private Port(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private final int m_port;
|
||||
private final int m_deviceAddress;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param port The I2C port the device is connected to.
|
||||
* @param deviceAddress The address of the device on the I2C bus.
|
||||
*/
|
||||
public I2C(Port port, int deviceAddress) {
|
||||
m_port = port.value;
|
||||
m_deviceAddress = deviceAddress;
|
||||
|
||||
I2CJNI.i2CInitialize((byte) port.value);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_I2C, deviceAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
public void free() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic transaction.
|
||||
*
|
||||
* <p>This is a lower-level interface to the I2C hardware giving you more control over each
|
||||
* transaction.
|
||||
*
|
||||
* @param dataToSend Buffer of data to send as part of the transaction.
|
||||
* @param sendSize Number of bytes to send as part of the transaction.
|
||||
* @param dataReceived Buffer to read data into.
|
||||
* @param receiveSize Number of bytes to read from the device.
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public synchronized boolean transaction(byte[] dataToSend, int sendSize,
|
||||
byte[] dataReceived, int receiveSize) {
|
||||
final int status;
|
||||
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(sendSize);
|
||||
if (sendSize > 0 && dataToSend != null) {
|
||||
dataToSendBuffer.put(dataToSend);
|
||||
}
|
||||
ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(receiveSize);
|
||||
|
||||
status = I2CJNI.i2CTransaction(m_port, (byte) m_deviceAddress, dataToSendBuffer,
|
||||
(byte) sendSize, dataReceivedBuffer, (byte) receiveSize);
|
||||
if (receiveSize > 0 && dataReceived != null) {
|
||||
dataReceivedBuffer.get(dataReceived);
|
||||
}
|
||||
return status < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic transaction.
|
||||
*
|
||||
* <p>This is a lower-level interface to the I2C hardware giving you more control over each
|
||||
* transaction.
|
||||
*
|
||||
* @param dataToSend Buffer of data to send as part of the transaction. Must be allocated using
|
||||
* ByteBuffer.allocateDirect().
|
||||
* @param sendSize Number of bytes to send as part of the transaction.
|
||||
* @param dataReceived Buffer to read data into. Must be allocated using {@link
|
||||
* ByteBuffer#allocateDirect(int)}.
|
||||
* @param receiveSize Number of bytes to read from the device.
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public synchronized boolean transaction(ByteBuffer dataToSend, int sendSize,
|
||||
ByteBuffer dataReceived, int receiveSize) {
|
||||
if (!dataToSend.isDirect()) {
|
||||
throw new IllegalArgumentException("dataToSend must be a direct buffer");
|
||||
}
|
||||
if (dataToSend.capacity() < sendSize) {
|
||||
throw new IllegalArgumentException("dataToSend is too small, must be at least " + sendSize);
|
||||
}
|
||||
if (!dataReceived.isDirect()) {
|
||||
throw new IllegalArgumentException("dataReceived must be a direct buffer");
|
||||
}
|
||||
if (dataReceived.capacity() < receiveSize) {
|
||||
throw new IllegalArgumentException(
|
||||
"dataReceived is too small, must be at least " + receiveSize);
|
||||
}
|
||||
|
||||
return I2CJNI.i2CTransaction(m_port, (byte) m_deviceAddress, dataToSend,
|
||||
(byte) sendSize, dataReceived, (byte) receiveSize) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to address a device on the I2C bus.
|
||||
*
|
||||
* <p>This allows you to figure out if there is a device on the I2C bus that responds to the
|
||||
* address specified in the constructor.
|
||||
*
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public boolean addressOnly() {
|
||||
return transaction((byte[]) null, (byte) 0, null, (byte) 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a write transaction with the device.
|
||||
*
|
||||
* <p>Write a single byte to a register on a device and wait until the transaction is complete.
|
||||
*
|
||||
* @param registerAddress The address of the register on the device to be written.
|
||||
* @param data The byte to write to the register on the device.
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public synchronized boolean write(int registerAddress, int data) {
|
||||
byte[] buffer = new byte[2];
|
||||
buffer[0] = (byte) registerAddress;
|
||||
buffer[1] = (byte) data;
|
||||
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(2);
|
||||
dataToSendBuffer.put(buffer);
|
||||
|
||||
return I2CJNI.i2CWrite(m_port, (byte) m_deviceAddress, dataToSendBuffer,
|
||||
(byte) buffer.length) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a write transaction with the device.
|
||||
*
|
||||
* <p>Write multiple bytes to a register on a device and wait until the transaction is complete.
|
||||
*
|
||||
* @param data The data to write to the device.
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public synchronized boolean writeBulk(byte[] data) {
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(data.length);
|
||||
dataToSendBuffer.put(data);
|
||||
|
||||
return I2CJNI.i2CWrite(m_port, (byte) m_deviceAddress, dataToSendBuffer,
|
||||
(byte) data.length) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a write transaction with the device.
|
||||
*
|
||||
* <p>Write multiple bytes to a register on a device and wait until the transaction is complete.
|
||||
*
|
||||
* @param data The data to write to the device. Must be created using ByteBuffer.allocateDirect().
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public synchronized boolean writeBulk(ByteBuffer data, int size) {
|
||||
if (!data.isDirect()) {
|
||||
throw new IllegalArgumentException("must be a direct buffer");
|
||||
}
|
||||
if (data.capacity() < size) {
|
||||
throw new IllegalArgumentException(
|
||||
"buffer is too small, must be at least " + size);
|
||||
}
|
||||
|
||||
return I2CJNI.i2CWrite(m_port, (byte) m_deviceAddress, data, (byte) size) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a read transaction with the device.
|
||||
*
|
||||
* <p>Read bytes from a device. Most I2C devices will auto-increment the register pointer
|
||||
* internally allowing you to read consecutive registers on a device in a single transaction.
|
||||
*
|
||||
* @param registerAddress The register to read first in the transaction.
|
||||
* @param count The number of bytes to read in the transaction.
|
||||
* @param buffer A pointer to the array of bytes to store the data read from the device.
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public boolean read(int registerAddress, int count, byte[] buffer) {
|
||||
requireNonNull(buffer, "Null return buffer was given");
|
||||
|
||||
if (count < 1) {
|
||||
throw new BoundaryException("Value must be at least 1, " + count + " given");
|
||||
}
|
||||
|
||||
byte[] registerAddressArray = new byte[1];
|
||||
registerAddressArray[0] = (byte) registerAddress;
|
||||
|
||||
return transaction(registerAddressArray, registerAddressArray.length, buffer, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a read transaction with the device.
|
||||
*
|
||||
* <p>Read bytes from a device. Most I2C devices will auto-increment the register pointer
|
||||
* internally allowing you to read consecutive registers on a device in a single transaction.
|
||||
*
|
||||
* @param registerAddress The register to read first in the transaction.
|
||||
* @param count The number of bytes to read in the transaction.
|
||||
* @param buffer A buffer to store the data read from the device. Must be created using
|
||||
* ByteBuffer.allocateDirect().
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public boolean read(int registerAddress, int count, ByteBuffer buffer) {
|
||||
if (count < 1) {
|
||||
throw new BoundaryException("Value must be at least 1, " + count + " given");
|
||||
}
|
||||
|
||||
if (!buffer.isDirect()) {
|
||||
throw new IllegalArgumentException("must be a direct buffer");
|
||||
}
|
||||
if (buffer.capacity() < count) {
|
||||
throw new IllegalArgumentException("buffer is too small, must be at least " + count);
|
||||
}
|
||||
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(1);
|
||||
dataToSendBuffer.put(0, (byte) registerAddress);
|
||||
|
||||
return transaction(dataToSendBuffer, 1, buffer, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a read only transaction with the device.
|
||||
*
|
||||
* <p>Read bytes from a device. This method does not write any data to prompt the device.
|
||||
*
|
||||
* @param buffer A pointer to the array of bytes to store the data read from the device.
|
||||
* @param count The number of bytes to read in the transaction.
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public boolean readOnly(byte[] buffer, int count) {
|
||||
requireNonNull(buffer, "Null return buffer was given");
|
||||
if (count < 1) {
|
||||
throw new BoundaryException("Value must be at least 1, " + count + " given");
|
||||
}
|
||||
|
||||
ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(count);
|
||||
|
||||
int retVal = I2CJNI.i2CRead(m_port, (byte) m_deviceAddress, dataReceivedBuffer,
|
||||
(byte) count);
|
||||
dataReceivedBuffer.get(buffer);
|
||||
return retVal < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a read only transaction with the device.
|
||||
*
|
||||
* <p>Read bytes from a device. This method does not write any data to prompt the device.
|
||||
*
|
||||
* @param buffer A pointer to the array of bytes to store the data read from the device. Must be
|
||||
* created using ByteBuffer.allocateDirect().
|
||||
* @param count The number of bytes to read in the transaction.
|
||||
* @return Transfer Aborted... false for success, true for aborted.
|
||||
*/
|
||||
public boolean readOnly(ByteBuffer buffer, int count) {
|
||||
if (count < 1) {
|
||||
throw new BoundaryException("Value must be at least 1, " + count
|
||||
+ " given");
|
||||
}
|
||||
|
||||
if (!buffer.isDirect()) {
|
||||
throw new IllegalArgumentException("must be a direct buffer");
|
||||
}
|
||||
if (buffer.capacity() < count) {
|
||||
throw new IllegalArgumentException("buffer is too small, must be at least " + count);
|
||||
}
|
||||
|
||||
return I2CJNI.i2CRead(m_port, (byte) m_deviceAddress, buffer, (byte) count)
|
||||
< 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a broadcast write to all devices on the I2C bus.
|
||||
*
|
||||
* <p>This is not currently implemented!
|
||||
*
|
||||
* @param registerAddress The register to write on all devices on the bus.
|
||||
* @param data The value to write to the devices.
|
||||
*/
|
||||
// public void broadcast(int registerAddress, int data) {
|
||||
// }
|
||||
|
||||
/**
|
||||
* Verify that a device's registers contain expected values.
|
||||
*
|
||||
* <p>Most devices will have a set of registers that contain a known value that can be used to
|
||||
* identify them. This allows an I2C device driver to easily verify that the device contains the
|
||||
* expected value.
|
||||
*
|
||||
* @param registerAddress The base register to start reading from the device.
|
||||
* @param count The size of the field to be verified.
|
||||
* @param expected A buffer containing the values expected from the device.
|
||||
* @return true if the sensor was verified to be connected
|
||||
* @pre The device must support and be configured to use register auto-increment.
|
||||
*/
|
||||
public boolean verifySensor(int registerAddress, int count,
|
||||
byte[] expected) {
|
||||
// TODO: Make use of all 7 read bytes
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(1);
|
||||
|
||||
ByteBuffer deviceData = ByteBuffer.allocateDirect(4);
|
||||
for (int i = 0, curRegisterAddress = registerAddress;
|
||||
i < count; i += 4, curRegisterAddress += 4) {
|
||||
int toRead = count - i < 4 ? count - i : 4;
|
||||
// Read the chunk of data. Return false if the sensor does not
|
||||
// respond.
|
||||
dataToSendBuffer.put(0, (byte) curRegisterAddress);
|
||||
if (transaction(dataToSendBuffer, 1, deviceData, toRead)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (byte j = 0; j < toRead; j++) {
|
||||
if (deviceData.get(j) != expected[i + j]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.InterruptJNI.InterruptJNIHandlerFunction;
|
||||
|
||||
|
||||
/**
|
||||
* It is recommended that you use this class in conjunction with classes from {@link
|
||||
* java.util.concurrent.atomic} as these objects are all thread safe.
|
||||
*
|
||||
* @param <T> The type of the parameter that should be returned to the the method {@link
|
||||
* #interruptFired(int, Object)}
|
||||
*/
|
||||
public abstract class InterruptHandlerFunction<T> {
|
||||
/**
|
||||
* The entry point for the interrupt. When the interrupt fires the {@link #apply(int, Object)}
|
||||
* method is called. The outer class is provided as an interface to allow the implementer to pass
|
||||
* a generic object to the interrupt fired method.
|
||||
*/
|
||||
private class Function implements InterruptJNIHandlerFunction {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void apply(int interruptAssertedMask, Object param) {
|
||||
interruptFired(interruptAssertedMask, (T) param);
|
||||
}
|
||||
}
|
||||
|
||||
final Function m_function = new Function();
|
||||
|
||||
/**
|
||||
* This method is run every time an interrupt is fired.
|
||||
*
|
||||
* @param interruptAssertedMask Interrupt Mask
|
||||
* @param param The parameter provided by overriding the {@link
|
||||
* #overridableParameter()} method.
|
||||
*/
|
||||
public abstract void interruptFired(int interruptAssertedMask, T param);
|
||||
|
||||
|
||||
/**
|
||||
* Override this method if you would like to pass a specific parameter to the {@link
|
||||
* #interruptFired(int, Object)} when it is fired by the interrupt. This method is called once
|
||||
* when {@link InterruptableSensorBase#requestInterrupts(InterruptHandlerFunction)} is run.
|
||||
*
|
||||
* @return The object that should be passed to the interrupt when it runs
|
||||
*/
|
||||
public T overridableParameter() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.InterruptJNI;
|
||||
import edu.wpi.first.wpilibj.util.AllocationException;
|
||||
|
||||
|
||||
/**
|
||||
* Base for sensors to be used with interrupts.
|
||||
*/
|
||||
public abstract class InterruptableSensorBase extends SensorBase {
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
public enum WaitResult {
|
||||
kTimeout(0x0), kRisingEdge(0x1), kFallingEdge(0x100), kBoth(0x101);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private WaitResult(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The interrupt resource.
|
||||
*/
|
||||
protected int m_interrupt = InterruptJNI.HalInvalidHandle;
|
||||
|
||||
/**
|
||||
* Flags if the interrupt being allocated is synchronous.
|
||||
*/
|
||||
protected boolean m_isSynchronousInterrupt = false;
|
||||
|
||||
/**
|
||||
* Create a new InterrupatableSensorBase.
|
||||
*/
|
||||
public InterruptableSensorBase() {
|
||||
m_interrupt = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is an analog trigger.
|
||||
*
|
||||
* @return true if this is an analog trigger.
|
||||
*/
|
||||
public abstract int getAnalogTriggerTypeForRouting();
|
||||
|
||||
/**
|
||||
* The channel routing number.
|
||||
*
|
||||
* @return channel routing number
|
||||
*/
|
||||
public abstract int getPortHandleForRouting();
|
||||
|
||||
/**
|
||||
* Request one of the 8 interrupts asynchronously on this digital input.
|
||||
*
|
||||
* @param handler The {@link InterruptHandlerFunction} that contains the method {@link
|
||||
* InterruptHandlerFunction#interruptFired(int, Object)} that will be called
|
||||
* whenever there is an interrupt on this device. Request interrupts in synchronous
|
||||
* mode where the user program interrupt handler will be called when an interrupt
|
||||
* occurs. The default is interrupt on rising edges only.
|
||||
*/
|
||||
public void requestInterrupts(InterruptHandlerFunction<?> handler) {
|
||||
if (m_interrupt != 0) {
|
||||
throw new AllocationException("The interrupt has already been allocated");
|
||||
}
|
||||
|
||||
allocateInterrupts(false);
|
||||
|
||||
assert m_interrupt != 0;
|
||||
|
||||
InterruptJNI.requestInterrupts(m_interrupt, getPortHandleForRouting(),
|
||||
getAnalogTriggerTypeForRouting());
|
||||
setUpSourceEdge(true, false);
|
||||
InterruptJNI.attachInterruptHandler(m_interrupt, handler.m_function,
|
||||
handler.overridableParameter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Request one of the 8 interrupts synchronously on this digital input. Request interrupts in
|
||||
* synchronous mode where the user program will have to explicitly wait for the interrupt to occur
|
||||
* using {@link #waitForInterrupt}. The default is interrupt on rising edges only.
|
||||
*/
|
||||
public void requestInterrupts() {
|
||||
if (m_interrupt != 0) {
|
||||
throw new AllocationException("The interrupt has already been allocated");
|
||||
}
|
||||
|
||||
allocateInterrupts(true);
|
||||
|
||||
assert m_interrupt != 0;
|
||||
|
||||
InterruptJNI.requestInterrupts(m_interrupt, getPortHandleForRouting(),
|
||||
getAnalogTriggerTypeForRouting());
|
||||
setUpSourceEdge(true, false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the interrupt.
|
||||
*
|
||||
* @param watcher true if the interrupt should be in synchronous mode where the user program will
|
||||
* have to explicitly wait for the interrupt to occur.
|
||||
*/
|
||||
protected void allocateInterrupts(boolean watcher) {
|
||||
m_isSynchronousInterrupt = watcher;
|
||||
|
||||
m_interrupt = InterruptJNI.initializeInterrupts(watcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel interrupts on this device. This deallocates all the chipobject structures and disables
|
||||
* any interrupts.
|
||||
*/
|
||||
public void cancelInterrupts() {
|
||||
if (m_interrupt == 0) {
|
||||
throw new IllegalStateException("The interrupt is not allocated.");
|
||||
}
|
||||
InterruptJNI.cleanInterrupts(m_interrupt);
|
||||
m_interrupt = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* In synchronous mode, wait for the defined interrupt to occur.
|
||||
*
|
||||
* @param timeout Timeout in seconds
|
||||
* @param ignorePrevious If true, ignore interrupts that happened before waitForInterrupt was
|
||||
* called.
|
||||
* @return Result of the wait.
|
||||
*/
|
||||
public WaitResult waitForInterrupt(double timeout, boolean ignorePrevious) {
|
||||
if (m_interrupt == 0) {
|
||||
throw new IllegalStateException("The interrupt is not allocated.");
|
||||
}
|
||||
int result = InterruptJNI.waitForInterrupt(m_interrupt, timeout, ignorePrevious);
|
||||
|
||||
// Rising edge result is the interrupt bit set in the byte 0xFF
|
||||
// Falling edge result is the interrupt bit set in the byte 0xFF00
|
||||
// Set any bit set to be true for that edge, and AND the 2 results
|
||||
// together to match the existing enum for all interrupts
|
||||
int rising = ((result & 0xFF) != 0) ? 0x1 : 0x0;
|
||||
int falling = ((result & 0xFF00) != 0) ? 0x0100 : 0x0;
|
||||
result = rising | falling;
|
||||
|
||||
for (WaitResult mode : WaitResult.values()) {
|
||||
if (mode.value == result) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* In synchronous mode, wait for the defined interrupt to occur.
|
||||
*
|
||||
* @param timeout Timeout in seconds
|
||||
* @return Result of the wait.
|
||||
*/
|
||||
public WaitResult waitForInterrupt(double timeout) {
|
||||
return waitForInterrupt(timeout, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable interrupts to occur on this input. Interrupts are disabled when the RequestInterrupt
|
||||
* call is made. This gives time to do the setup of the other options before starting to field
|
||||
* interrupts.
|
||||
*/
|
||||
public void enableInterrupts() {
|
||||
if (m_interrupt == 0) {
|
||||
throw new IllegalStateException("The interrupt is not allocated.");
|
||||
}
|
||||
if (m_isSynchronousInterrupt) {
|
||||
throw new IllegalStateException("You do not need to enable synchronous interrupts");
|
||||
}
|
||||
InterruptJNI.enableInterrupts(m_interrupt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable Interrupts without without deallocating structures.
|
||||
*/
|
||||
public void disableInterrupts() {
|
||||
if (m_interrupt == 0) {
|
||||
throw new IllegalStateException("The interrupt is not allocated.");
|
||||
}
|
||||
if (m_isSynchronousInterrupt) {
|
||||
throw new IllegalStateException("You can not disable synchronous interrupts");
|
||||
}
|
||||
InterruptJNI.disableInterrupts(m_interrupt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timestamp for the rising interrupt that occurred most recently. This is in the same
|
||||
* time domain as getClock(). The rising-edge interrupt should be enabled with {@link
|
||||
* #setUpSourceEdge}.
|
||||
*
|
||||
* @return Timestamp in seconds since boot.
|
||||
*/
|
||||
public double readRisingTimestamp() {
|
||||
if (m_interrupt == 0) {
|
||||
throw new IllegalStateException("The interrupt is not allocated.");
|
||||
}
|
||||
return InterruptJNI.readInterruptRisingTimestamp(m_interrupt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timestamp for the falling interrupt that occurred most recently. This is in the same
|
||||
* time domain as getClock(). The falling-edge interrupt should be enabled with {@link
|
||||
* #setUpSourceEdge}.
|
||||
*
|
||||
* @return Timestamp in seconds since boot.
|
||||
*/
|
||||
public double readFallingTimestamp() {
|
||||
if (m_interrupt == 0) {
|
||||
throw new IllegalStateException("The interrupt is not allocated.");
|
||||
}
|
||||
return InterruptJNI.readInterruptFallingTimestamp(m_interrupt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which edge to trigger interrupts on.
|
||||
*
|
||||
* @param risingEdge true to interrupt on rising edge
|
||||
* @param fallingEdge true to interrupt on falling edge
|
||||
*/
|
||||
public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
|
||||
if (m_interrupt != 0) {
|
||||
InterruptJNI.setInterruptUpSourceEdge(m_interrupt, risingEdge,
|
||||
fallingEdge);
|
||||
} else {
|
||||
throw new IllegalArgumentException("You must call RequestInterrupts before setUpSourceEdge");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tInstances;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
|
||||
/**
|
||||
* IterativeRobot implements the IterativeRobotBase robot program framework.
|
||||
*
|
||||
* <p>The IterativeRobot class is intended to be subclassed by a user creating a robot program.
|
||||
*
|
||||
* <p>periodic() functions from the base class are called each time a new packet is received from
|
||||
* the driver station.
|
||||
*/
|
||||
public class IterativeRobot extends IterativeRobotBase {
|
||||
public IterativeRobot() {
|
||||
HAL.report(tResourceType.kResourceType_Framework, tInstances.kFramework_Iterative);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an alternate "main loop" via startCompetition().
|
||||
*/
|
||||
public void startCompetition() {
|
||||
// loop forever, calling the appropriate mode-dependent function
|
||||
while (true) {
|
||||
// Wait for new data to arrive
|
||||
m_ds.waitForData();
|
||||
|
||||
loopFunc();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. 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.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* IterativeRobotBase implements a specific type of robot program framework, extending the RobotBase
|
||||
* class.
|
||||
*
|
||||
* <p>The IterativeRobotBase class does not implement startCompetition(), so it should not be used
|
||||
* by teams directly.
|
||||
*
|
||||
* <p>This class provides the following functions which are called by the main loop,
|
||||
* startCompetition(), at the appropriate times:
|
||||
*
|
||||
* <p>robotInit() -- provide for initialization at robot power-on
|
||||
*
|
||||
* <p>init() functions -- each of the following functions is called once when the
|
||||
* appropriate mode is entered:
|
||||
* - disabledInit() -- called only when first disabled
|
||||
* - autonomousInit() -- called each and every time autonomous is entered from
|
||||
* another mode
|
||||
* - teleopInit() -- called each and every time teleop is entered from
|
||||
* another mode
|
||||
* - testInit() -- called each and every time test is entered from
|
||||
* another mode
|
||||
*
|
||||
* <p>periodic() functions -- each of these functions is called on an interval:
|
||||
* - robotPeriodic()
|
||||
* - disabledPeriodic()
|
||||
* - autonomousPeriodic()
|
||||
* - teleopPeriodic()
|
||||
* - testPeriodic()
|
||||
*/
|
||||
public abstract class IterativeRobotBase extends RobotBase {
|
||||
private enum Mode {
|
||||
kNone,
|
||||
kDisabled,
|
||||
kAutonomous,
|
||||
kTeleop,
|
||||
kTest
|
||||
}
|
||||
|
||||
private Mode m_lastMode = Mode.kNone;
|
||||
|
||||
/**
|
||||
* IterativeRobotBase constructor.
|
||||
*/
|
||||
public IterativeRobotBase() {
|
||||
robotInit();
|
||||
|
||||
// Tell the DS that the robot is ready to be enabled
|
||||
HAL.observeUserProgramStarting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an alternate "main loop" via startCompetition().
|
||||
*/
|
||||
public abstract void startCompetition();
|
||||
|
||||
/* ----------- Overridable initialization code ----------------- */
|
||||
|
||||
/**
|
||||
* Robot-wide initialization code should go here.
|
||||
*
|
||||
* <p>Users should override this method for default Robot-wide initialization which will be called
|
||||
* when the robot is first powered on. It will be called exactly one time.
|
||||
*
|
||||
* <p>Warning: the Driver Station "Robot Code" light and FMS "Robot Ready" indicators will be off
|
||||
* until RobotInit() exits. Code in RobotInit() that waits for enable will cause the robot to
|
||||
* never indicate that the code is ready, causing the robot to be bypassed in a match.
|
||||
*/
|
||||
public void robotInit() {
|
||||
System.out.println("Default robotInit() method... Overload me!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization code for disabled mode should go here.
|
||||
*
|
||||
* <p>Users should override this method for initialization code which will be called each time the
|
||||
* robot enters disabled mode.
|
||||
*/
|
||||
public void disabledInit() {
|
||||
System.out.println("Default disabledInit() method... Overload me!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization code for autonomous mode should go here.
|
||||
*
|
||||
* <p>Users should override this method for initialization code which will be called each time the
|
||||
* robot enters autonomous mode.
|
||||
*/
|
||||
public void autonomousInit() {
|
||||
System.out.println("Default autonomousInit() method... Overload me!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization code for teleop mode should go here.
|
||||
*
|
||||
* <p>Users should override this method for initialization code which will be called each time the
|
||||
* robot enters teleop mode.
|
||||
*/
|
||||
public void teleopInit() {
|
||||
System.out.println("Default teleopInit() method... Overload me!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization code for test mode should go here.
|
||||
*
|
||||
* <p>Users should override this method for initialization code which will be called each time the
|
||||
* robot enters test mode.
|
||||
*/
|
||||
@SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation")
|
||||
public void testInit() {
|
||||
System.out.println("Default testInit() method... Overload me!");
|
||||
}
|
||||
|
||||
/* ----------- Overridable periodic code ----------------- */
|
||||
|
||||
private boolean m_rpFirstRun = true;
|
||||
|
||||
/**
|
||||
* Periodic code for all robot modes should go here.
|
||||
*/
|
||||
public void robotPeriodic() {
|
||||
if (m_rpFirstRun) {
|
||||
System.out.println("Default robotPeriodic() method... Overload me!");
|
||||
m_rpFirstRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean m_dpFirstRun = true;
|
||||
|
||||
/**
|
||||
* Periodic code for disabled mode should go here.
|
||||
*/
|
||||
public void disabledPeriodic() {
|
||||
if (m_dpFirstRun) {
|
||||
System.out.println("Default disabledPeriodic() method... Overload me!");
|
||||
m_dpFirstRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean m_apFirstRun = true;
|
||||
|
||||
/**
|
||||
* Periodic code for autonomous mode should go here.
|
||||
*/
|
||||
public void autonomousPeriodic() {
|
||||
if (m_apFirstRun) {
|
||||
System.out.println("Default autonomousPeriodic() method... Overload me!");
|
||||
m_apFirstRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean m_tpFirstRun = true;
|
||||
|
||||
/**
|
||||
* Periodic code for teleop mode should go here.
|
||||
*/
|
||||
public void teleopPeriodic() {
|
||||
if (m_tpFirstRun) {
|
||||
System.out.println("Default teleopPeriodic() method... Overload me!");
|
||||
m_tpFirstRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean m_tmpFirstRun = true;
|
||||
|
||||
/**
|
||||
* Periodic code for test mode should go here.
|
||||
*/
|
||||
@SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation")
|
||||
public void testPeriodic() {
|
||||
if (m_tmpFirstRun) {
|
||||
System.out.println("Default testPeriodic() method... Overload me!");
|
||||
m_tmpFirstRun = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected void loopFunc() {
|
||||
// Call the appropriate function depending upon the current robot mode
|
||||
if (isDisabled()) {
|
||||
// call DisabledInit() if we are now just entering disabled mode from
|
||||
// either a different mode or from power-on
|
||||
if (m_lastMode != Mode.kDisabled) {
|
||||
LiveWindow.setEnabled(false);
|
||||
disabledInit();
|
||||
m_lastMode = Mode.kDisabled;
|
||||
}
|
||||
HAL.observeUserProgramDisabled();
|
||||
disabledPeriodic();
|
||||
} else if (isAutonomous()) {
|
||||
// call Autonomous_Init() if this is the first time
|
||||
// we've entered autonomous_mode
|
||||
if (m_lastMode != Mode.kAutonomous) {
|
||||
LiveWindow.setEnabled(false);
|
||||
// KBS NOTE: old code reset all PWMs and relays to "safe values"
|
||||
// whenever entering autonomous mode, before calling
|
||||
// "Autonomous_Init()"
|
||||
autonomousInit();
|
||||
m_lastMode = Mode.kAutonomous;
|
||||
}
|
||||
HAL.observeUserProgramAutonomous();
|
||||
autonomousPeriodic();
|
||||
} else if (isOperatorControl()) {
|
||||
// call Teleop_Init() if this is the first time
|
||||
// we've entered teleop_mode
|
||||
if (m_lastMode != Mode.kTeleop) {
|
||||
LiveWindow.setEnabled(false);
|
||||
teleopInit();
|
||||
m_lastMode = Mode.kTeleop;
|
||||
}
|
||||
HAL.observeUserProgramTeleop();
|
||||
teleopPeriodic();
|
||||
} else {
|
||||
// call TestInit() if we are now just entering test mode from either
|
||||
// a different mode or from power-on
|
||||
if (m_lastMode != Mode.kTest) {
|
||||
LiveWindow.setEnabled(true);
|
||||
testInit();
|
||||
m_lastMode = Mode.kTest;
|
||||
}
|
||||
HAL.observeUserProgramTest();
|
||||
testPeriodic();
|
||||
}
|
||||
robotPeriodic();
|
||||
}
|
||||
}
|
||||
44
wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java
Normal file
44
wpilibj/src/main/java/edu/wpi/first/wpilibj/Jaguar.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* Texas Instruments / Vex Robotics Jaguar Speed Controller as a PWM device.
|
||||
*/
|
||||
public class Jaguar extends PWMSpeedController {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param channel The PWM channel that the Jaguar is attached to. 0-9 are on-board, 10-19 are on
|
||||
* the MXP port
|
||||
*/
|
||||
public Jaguar(final int channel) {
|
||||
super(channel);
|
||||
|
||||
/*
|
||||
* Input profile defined by Luminary Micro.
|
||||
*
|
||||
* Full reverse ranges from 0.671325ms to 0.6972211ms Proportional reverse
|
||||
* ranges from 0.6972211ms to 1.4482078ms Neutral ranges from 1.4482078ms to
|
||||
* 1.5517922ms Proportional forward ranges from 1.5517922ms to 2.3027789ms
|
||||
* Full forward ranges from 2.3027789ms to 2.328675ms
|
||||
*/
|
||||
setBounds(2.31, 1.55, 1.507, 1.454, .697);
|
||||
setPeriodMultiplier(PeriodMultiplier.k1X);
|
||||
setSpeed(0.0);
|
||||
setZeroLatch();
|
||||
|
||||
HAL.report(tResourceType.kResourceType_Jaguar, getChannel());
|
||||
LiveWindow.addActuator("Jaguar", getChannel(), this);
|
||||
}
|
||||
}
|
||||
398
wpilibj/src/main/java/edu/wpi/first/wpilibj/Joystick.java
Normal file
398
wpilibj/src/main/java/edu/wpi/first/wpilibj/Joystick.java
Normal file
@@ -0,0 +1,398 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
|
||||
/**
|
||||
* Handle input from standard Joysticks connected to the Driver Station. This class handles standard
|
||||
* input that comes from the Driver Station. Each time a value is requested the most recent value is
|
||||
* returned. There is a single class instance for each joystick and the mapping of ports to hardware
|
||||
* buttons depends on the code in the Driver Station.
|
||||
*/
|
||||
public class Joystick extends JoystickBase {
|
||||
|
||||
static final byte kDefaultXAxis = 0;
|
||||
static final byte kDefaultYAxis = 1;
|
||||
static final byte kDefaultZAxis = 2;
|
||||
static final byte kDefaultTwistAxis = 2;
|
||||
static final byte kDefaultThrottleAxis = 3;
|
||||
static final int kDefaultTriggerButton = 1;
|
||||
static final int kDefaultTopButton = 2;
|
||||
|
||||
/**
|
||||
* Represents an analog axis on a joystick.
|
||||
*/
|
||||
public enum AxisType {
|
||||
kX(0), kY(1), kZ(2), kTwist(3), kThrottle(4), kNumAxis(5);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private AxisType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a digital button on the JoyStick.
|
||||
*/
|
||||
public enum ButtonType {
|
||||
kTrigger(0), kTop(1), kNumButton(2);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private ButtonType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private final DriverStation m_ds;
|
||||
private final byte[] m_axes;
|
||||
private final byte[] m_buttons;
|
||||
private int m_outputs;
|
||||
private short m_leftRumble;
|
||||
private short m_rightRumble;
|
||||
|
||||
/**
|
||||
* Construct an instance of a joystick. The joystick index is the USB port on the drivers
|
||||
* station.
|
||||
*
|
||||
* @param port The port on the Driver Station that the joystick is plugged into.
|
||||
*/
|
||||
public Joystick(final int port) {
|
||||
this(port, AxisType.kNumAxis.value, ButtonType.kNumButton.value);
|
||||
|
||||
m_axes[AxisType.kX.value] = kDefaultXAxis;
|
||||
m_axes[AxisType.kY.value] = kDefaultYAxis;
|
||||
m_axes[AxisType.kZ.value] = kDefaultZAxis;
|
||||
m_axes[AxisType.kTwist.value] = kDefaultTwistAxis;
|
||||
m_axes[AxisType.kThrottle.value] = kDefaultThrottleAxis;
|
||||
|
||||
m_buttons[ButtonType.kTrigger.value] = kDefaultTriggerButton;
|
||||
m_buttons[ButtonType.kTop.value] = kDefaultTopButton;
|
||||
|
||||
HAL.report(tResourceType.kResourceType_Joystick, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected version of the constructor to be called by sub-classes.
|
||||
*
|
||||
* <p>This constructor allows the subclass to configure the number of constants for axes and
|
||||
* buttons.
|
||||
*
|
||||
* @param port The port on the Driver Station that the joystick is plugged into.
|
||||
* @param numAxisTypes The number of axis types in the enum.
|
||||
* @param numButtonTypes The number of button types in the enum.
|
||||
*/
|
||||
protected Joystick(int port, int numAxisTypes, int numButtonTypes) {
|
||||
super(port);
|
||||
|
||||
m_ds = DriverStation.getInstance();
|
||||
m_axes = new byte[numAxisTypes];
|
||||
m_buttons = new byte[numButtonTypes];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the X value of the joystick. This depends on the mapping of the joystick connected to the
|
||||
* current port.
|
||||
*
|
||||
* @param hand Unused
|
||||
* @return The X value of the joystick.
|
||||
*/
|
||||
@Override
|
||||
public final double getX(Hand hand) {
|
||||
return getRawAxis(m_axes[AxisType.kX.value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Y value of the joystick. This depends on the mapping of the joystick connected to the
|
||||
* current port.
|
||||
*
|
||||
* @param hand Unused
|
||||
* @return The Y value of the joystick.
|
||||
*/
|
||||
@Override
|
||||
public final double getY(Hand hand) {
|
||||
return getRawAxis(m_axes[AxisType.kY.value]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final double getZ(Hand hand) {
|
||||
return getRawAxis(m_axes[AxisType.kZ.value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the twist value of the current joystick. This depends on the mapping of the joystick
|
||||
* connected to the current port.
|
||||
*
|
||||
* @return The Twist value of the joystick.
|
||||
*/
|
||||
public double getTwist() {
|
||||
return getRawAxis(m_axes[AxisType.kTwist.value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the throttle value of the current joystick. This depends on the mapping of the joystick
|
||||
* connected to the current port.
|
||||
*
|
||||
* @return The Throttle value of the joystick.
|
||||
*/
|
||||
public double getThrottle() {
|
||||
return getRawAxis(m_axes[AxisType.kThrottle.value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the axis.
|
||||
*
|
||||
* @param axis The axis to read, starting at 0.
|
||||
* @return The value of the axis.
|
||||
*/
|
||||
public double getRawAxis(final int axis) {
|
||||
return m_ds.getStickAxis(getPort(), axis);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the current joystick, return the axis determined by the argument.
|
||||
*
|
||||
* <p>This is for cases where the joystick axis is returned programatically, otherwise one of the
|
||||
* previous functions would be preferable (for example getX()).
|
||||
*
|
||||
* @param axis The axis to read.
|
||||
* @return The value of the axis.
|
||||
*/
|
||||
public double getAxis(final AxisType axis) {
|
||||
switch (axis) {
|
||||
case kX:
|
||||
return getX();
|
||||
case kY:
|
||||
return getY();
|
||||
case kZ:
|
||||
return getZ();
|
||||
case kTwist:
|
||||
return getTwist();
|
||||
case kThrottle:
|
||||
return getThrottle();
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For the current joystick, return the number of axis.
|
||||
*/
|
||||
public int getAxisCount() {
|
||||
return m_ds.getStickAxisCount(getPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the state of the trigger on the joystick.
|
||||
*
|
||||
* <p>Look up which button has been assigned to the trigger and read its state.
|
||||
*
|
||||
* @param hand This parameter is ignored for the Joystick class and is only here to complete the
|
||||
* GenericHID interface.
|
||||
* @return The state of the trigger.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public boolean getTrigger(Hand hand) {
|
||||
return getRawButton(m_buttons[ButtonType.kTrigger.value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the state of the top button on the joystick.
|
||||
*
|
||||
* <p>Look up which button has been assigned to the top and read its state.
|
||||
*
|
||||
* @param hand This parameter is ignored for the Joystick class and is only here to complete the
|
||||
* GenericHID interface.
|
||||
* @return The state of the top button.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public boolean getTop(Hand hand) {
|
||||
return getRawButton(m_buttons[ButtonType.kTop.value]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPOV(int pov) {
|
||||
return m_ds.getStickPOV(getPort(), pov);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPOVCount() {
|
||||
return m_ds.getStickPOVCount(getPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not supported for the Joystick. This method is only here to complete the GenericHID
|
||||
* interface.
|
||||
*
|
||||
* @param hand This parameter is ignored for the Joystick class and is only here to complete the
|
||||
* GenericHID interface.
|
||||
* @return The state of the bumper (always false)
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public boolean getBumper(Hand hand) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the button value (starting at button 1).
|
||||
*
|
||||
* <p>The appropriate button is returned as a boolean value.
|
||||
*
|
||||
* @param button The button number to be read (starting at 1).
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getRawButton(final int button) {
|
||||
return m_ds.getStickButton(getPort(), (byte) button);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the current joystick, return the number of buttons.
|
||||
*/
|
||||
public int getButtonCount() {
|
||||
return m_ds.getStickButtonCount(getPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get buttons based on an enumerated type.
|
||||
*
|
||||
* <p>The button type will be looked up in the list of buttons and then read.
|
||||
*
|
||||
* @param button The type of button to read.
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getButton(ButtonType button) {
|
||||
switch (button) {
|
||||
case kTrigger:
|
||||
return getTrigger();
|
||||
case kTop:
|
||||
return getTop();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the magnitude of the direction vector formed by the joystick's current position relative to
|
||||
* its origin.
|
||||
*
|
||||
* @return The magnitude of the direction vector
|
||||
*/
|
||||
public double getMagnitude() {
|
||||
return Math.sqrt(Math.pow(getX(), 2) + Math.pow(getY(), 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direction of the vector formed by the joystick and its origin in radians.
|
||||
*
|
||||
* @return The direction of the vector in radians
|
||||
*/
|
||||
public double getDirectionRadians() {
|
||||
return Math.atan2(getX(), -getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direction of the vector formed by the joystick and its origin in degrees.
|
||||
*
|
||||
* <p>Uses acos(-1) to represent Pi due to absence of readily accessable Pi constant in C++
|
||||
*
|
||||
* @return The direction of the vector in degrees
|
||||
*/
|
||||
public double getDirectionDegrees() {
|
||||
return Math.toDegrees(getDirectionRadians());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channel currently associated with the specified axis.
|
||||
*
|
||||
* @param axis The axis to look up the channel for.
|
||||
* @return The channel fr the axis.
|
||||
*/
|
||||
public int getAxisChannel(AxisType axis) {
|
||||
return m_axes[axis.value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the channel associated with a specified axis.
|
||||
*
|
||||
* @param axis The axis to set the channel for.
|
||||
* @param channel The channel to set the axis to.
|
||||
*/
|
||||
public void setAxisChannel(AxisType axis, int channel) {
|
||||
m_axes[axis.value] = (byte) channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of isXbox for the current joystick.
|
||||
*
|
||||
* @return A boolean that is true if the controller is an xbox controller.
|
||||
*/
|
||||
public boolean getIsXbox() {
|
||||
return m_ds.getJoystickIsXbox(getPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the axis type of a joystick axis.
|
||||
*
|
||||
* @return the axis type of a joystick axis.
|
||||
*/
|
||||
public int getAxisType(int axis) {
|
||||
return m_ds.getJoystickAxisType(getPort(), axis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the HID.
|
||||
*
|
||||
* @return the type of the HID.
|
||||
*/
|
||||
@Override
|
||||
public HIDType getType() {
|
||||
return HIDType.values()[m_ds.getJoystickType(getPort())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the HID.
|
||||
*
|
||||
* @return the name of the HID.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return m_ds.getJoystickName(getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutput(int outputNumber, boolean value) {
|
||||
m_outputs = (m_outputs & ~(1 << (outputNumber - 1))) | ((value ? 1 : 0) << (outputNumber - 1));
|
||||
HAL.setJoystickOutputs((byte) getPort(), m_outputs, m_leftRumble, m_rightRumble);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutputs(int value) {
|
||||
m_outputs = value;
|
||||
HAL.setJoystickOutputs((byte) getPort(), m_outputs, m_leftRumble, m_rightRumble);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRumble(RumbleType type, double value) {
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
} else if (value > 1) {
|
||||
value = 1;
|
||||
}
|
||||
if (type == RumbleType.kLeftRumble) {
|
||||
m_leftRumble = (short) (value * 65535);
|
||||
} else {
|
||||
m_rightRumble = (short) (value * 65535);
|
||||
}
|
||||
HAL.setJoystickOutputs((byte) getPort(), m_outputs, m_leftRumble, m_rightRumble);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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;
|
||||
|
||||
/**
|
||||
* JoystickBase Interface.
|
||||
*/
|
||||
public abstract class JoystickBase extends GenericHID {
|
||||
public JoystickBase(int port) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the z position of the HID.
|
||||
*
|
||||
* @param hand which hand, left or right
|
||||
* @return the z position
|
||||
*/
|
||||
public abstract double getZ(Hand hand);
|
||||
|
||||
public double getZ() {
|
||||
return getZ(Hand.kRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the twist value.
|
||||
*
|
||||
* @return the twist value
|
||||
*/
|
||||
public abstract double getTwist();
|
||||
|
||||
/**
|
||||
* Get the throttle.
|
||||
*
|
||||
* @return the throttle value
|
||||
*/
|
||||
public abstract double getThrottle();
|
||||
|
||||
/**
|
||||
* Is the trigger pressed.
|
||||
*
|
||||
* @return true if pressed
|
||||
*/
|
||||
public final boolean getTrigger() {
|
||||
return getTrigger(Hand.kRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the trigger pressed.
|
||||
*
|
||||
* @param hand which hand
|
||||
* @return true if the trigger for the given hand is pressed
|
||||
*/
|
||||
public abstract boolean getTrigger(Hand hand);
|
||||
|
||||
/**
|
||||
* Is the top button pressed.
|
||||
*
|
||||
* @return true if the top button is pressed
|
||||
*/
|
||||
public final boolean getTop() {
|
||||
return getTop(Hand.kRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the top button pressed.
|
||||
*
|
||||
* @param hand which hand
|
||||
* @return true if hte top button for the given hand is pressed
|
||||
*/
|
||||
public abstract boolean getTop(Hand hand);
|
||||
|
||||
public abstract int getPOV(int pov);
|
||||
|
||||
public abstract int getPOVCount();
|
||||
|
||||
public abstract HIDType getType();
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public abstract void setOutput(int outputNumber, boolean value);
|
||||
|
||||
public abstract void setOutputs(int value);
|
||||
|
||||
public abstract void setRumble(RumbleType type, double value);
|
||||
}
|
||||
29
wpilibj/src/main/java/edu/wpi/first/wpilibj/MotorSafety.java
Normal file
29
wpilibj/src/main/java/edu/wpi/first/wpilibj/MotorSafety.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* Shuts off motors when their outputs aren't updated often enough.
|
||||
*/
|
||||
public interface MotorSafety {
|
||||
double DEFAULT_SAFETY_EXPIRATION = 0.1;
|
||||
|
||||
void setExpiration(double timeout);
|
||||
|
||||
double getExpiration();
|
||||
|
||||
boolean isAlive();
|
||||
|
||||
void stopMotor();
|
||||
|
||||
void setSafetyEnabled(boolean enabled);
|
||||
|
||||
boolean isSafetyEnabled();
|
||||
|
||||
String getDescription();
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* The MotorSafetyHelper object is constructed for every object that wants to implement the Motor
|
||||
* Safety protocol. The helper object has the code to actually do the timing and call the motors
|
||||
* Stop() method when the timeout expires. The motor object is expected to call the Feed() method
|
||||
* whenever the motors value is updated.
|
||||
*/
|
||||
public final class MotorSafetyHelper {
|
||||
|
||||
private double m_expiration;
|
||||
private boolean m_enabled;
|
||||
private double m_stopTime;
|
||||
private final MotorSafety m_safeObject;
|
||||
private final MotorSafetyHelper m_nextHelper;
|
||||
private static MotorSafetyHelper headHelper = null;
|
||||
|
||||
/**
|
||||
* The constructor for a MotorSafetyHelper object. The helper object is constructed for every
|
||||
* object that wants to implement the Motor Safety protocol. The helper object has the code to
|
||||
* actually do the timing and call the motors Stop() method when the timeout expires. The motor
|
||||
* object is expected to call the Feed() method whenever the motors value is updated.
|
||||
*
|
||||
* @param safeObject a pointer to the motor object implementing MotorSafety. This is used to call
|
||||
* the Stop() method on the motor.
|
||||
*/
|
||||
public MotorSafetyHelper(MotorSafety safeObject) {
|
||||
m_safeObject = safeObject;
|
||||
m_enabled = false;
|
||||
m_expiration = MotorSafety.DEFAULT_SAFETY_EXPIRATION;
|
||||
m_stopTime = Timer.getFPGATimestamp();
|
||||
m_nextHelper = headHelper;
|
||||
headHelper = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed the motor safety object. Resets the timer on this object that is used to do the timeouts.
|
||||
*/
|
||||
public void feed() {
|
||||
m_stopTime = Timer.getFPGATimestamp() + m_expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expiration time for the corresponding motor safety object.
|
||||
*
|
||||
* @param expirationTime The timeout value in seconds.
|
||||
*/
|
||||
public void setExpiration(double expirationTime) {
|
||||
m_expiration = expirationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the timeout value for the corresponding motor safety object.
|
||||
*
|
||||
* @return the timeout value in seconds.
|
||||
*/
|
||||
public double getExpiration() {
|
||||
return m_expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine of the motor is still operating or has timed out.
|
||||
*
|
||||
* @return a true value if the motor is still operating normally and hasn't timed out.
|
||||
*/
|
||||
public boolean isAlive() {
|
||||
return !m_enabled || m_stopTime > Timer.getFPGATimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this motor has exceeded its timeout. This method is called periodically to determine
|
||||
* if this motor has exceeded its timeout value. If it has, the stop method is called, and the
|
||||
* motor is shut down until its value is updated again.
|
||||
*/
|
||||
public void check() {
|
||||
if (!m_enabled || RobotState.isDisabled() || RobotState.isTest()) {
|
||||
return;
|
||||
}
|
||||
if (m_stopTime < Timer.getFPGATimestamp()) {
|
||||
DriverStation.reportError(m_safeObject.getDescription() + "... Output not updated often "
|
||||
+ "enough.", false);
|
||||
|
||||
m_safeObject.stopMotor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable motor safety for this device Turn on and off the motor safety option for this
|
||||
* PWM object.
|
||||
*
|
||||
* @param enabled True if motor safety is enforced for this object
|
||||
*/
|
||||
public void setSafetyEnabled(boolean enabled) {
|
||||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the state of the motor safety enabled flag Return if the motor safety is currently
|
||||
* enabled for this devicce.
|
||||
*
|
||||
* @return True if motor safety is enforced for this device
|
||||
*/
|
||||
public boolean isSafetyEnabled() {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the motors to see if any have timed out. This static method is called periodically to
|
||||
* poll all the motors and stop any that have timed out.
|
||||
*/
|
||||
// TODO: these should be synchronized with the setting methods in case it's
|
||||
// called from a different thread
|
||||
public static void checkMotors() {
|
||||
for (MotorSafetyHelper msh = headHelper; msh != null; msh = msh.m_nextHelper) {
|
||||
msh.check();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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;
|
||||
|
||||
|
||||
/**
|
||||
* The interface for sendable objects that gives the sendable a default name in the Smart
|
||||
* Dashboard.
|
||||
*/
|
||||
public interface NamedSendable extends Sendable {
|
||||
|
||||
/**
|
||||
* The name of the subtable.
|
||||
*
|
||||
* @return the name of the subtable of SmartDashboard that the Sendable object will use.
|
||||
*/
|
||||
String getName();
|
||||
}
|
||||
140
wpilibj/src/main/java/edu/wpi/first/wpilibj/Notifier.java
Normal file
140
wpilibj/src/main/java/edu/wpi/first/wpilibj/Notifier.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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 java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.NotifierJNI;
|
||||
|
||||
public class Notifier {
|
||||
|
||||
private static class Process implements NotifierJNI.NotifierJNIHandlerFunction {
|
||||
// The lock for the process information.
|
||||
private final ReentrantLock m_processLock = new ReentrantLock();
|
||||
// The C pointer to the notifier object. We don't use it directly, it is
|
||||
// just passed to the JNI bindings.
|
||||
AtomicInteger m_notifier = new AtomicInteger();
|
||||
// The time, in microseconds, at which the corresponding handler should be
|
||||
// called. Has the same zero as Utility.getFPGATime().
|
||||
private double m_expirationTime = 0;
|
||||
// The handler passed in by the user which should be called at the
|
||||
// appropriate interval.
|
||||
private Runnable m_handler;
|
||||
// Whether we are calling the handler just once or periodically.
|
||||
private boolean m_periodic = false;
|
||||
// If periodic, the period of the calling; if just once, stores how long it
|
||||
// is until we call the handler.
|
||||
private double m_period = 0;
|
||||
// Lock on the handler so that the handler is not called before it has
|
||||
// completed. This is only relevant if the handler takes a very long time
|
||||
// to complete (or the period is very short) and when everything is being
|
||||
// destructed.
|
||||
private final ReentrantLock m_handlerLock = new ReentrantLock();
|
||||
|
||||
public Process(Runnable run) {
|
||||
m_handler = run;
|
||||
m_notifier.set(NotifierJNI.initializeNotifier(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NoFinalizer")
|
||||
protected void finalize() {
|
||||
int handle = m_notifier.getAndSet(0);
|
||||
NotifierJNI.cleanNotifier(handle);
|
||||
m_handlerLock.lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the alarm hardware to reflect the next alarm.
|
||||
*/
|
||||
private void updateAlarm() {
|
||||
NotifierJNI.updateNotifierAlarm(m_notifier.get(), (long) (m_expirationTime * 1e6));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler which is called by the HAL library; it handles the subsequent calling of the user
|
||||
* handler.
|
||||
*/
|
||||
@Override
|
||||
public void apply(long time) {
|
||||
m_processLock.lock();
|
||||
if (m_periodic) {
|
||||
m_expirationTime += m_period;
|
||||
updateAlarm();
|
||||
}
|
||||
|
||||
m_handlerLock.lock();
|
||||
m_processLock.unlock();
|
||||
|
||||
m_handler.run();
|
||||
m_handlerLock.unlock();
|
||||
}
|
||||
|
||||
public void start(double period, boolean periodic) {
|
||||
synchronized (m_processLock) {
|
||||
m_periodic = periodic;
|
||||
m_period = period;
|
||||
m_expirationTime = Utility.getFPGATime() * 1e-6 + period;
|
||||
updateAlarm();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
NotifierJNI.stopNotifierAlarm(m_notifier.get());
|
||||
|
||||
// Wait for a currently executing handler to complete before returning
|
||||
// from stop()
|
||||
m_handlerLock.lock();
|
||||
m_handlerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private Process m_process;
|
||||
|
||||
/**
|
||||
* Create a Notifier for timer event notification.
|
||||
*
|
||||
* @param run The handler that is called at the notification time which is set using StartSingle
|
||||
* or StartPeriodic.
|
||||
*/
|
||||
public Notifier(Runnable run) {
|
||||
m_process = new Process(run);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register for single event notification. A timer event is queued for a single event after the
|
||||
* specified delay.
|
||||
*
|
||||
* @param delay Seconds to wait before the handler is called.
|
||||
*/
|
||||
public void startSingle(double delay) {
|
||||
m_process.start(delay, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register for periodic event notification. A timer event is queued for periodic event
|
||||
* notification. Each time the interrupt occurs, the event will be immediately requeued for the
|
||||
* same time interval.
|
||||
*
|
||||
* @param period Period in seconds to call the handler starting one period after the call to this
|
||||
* method.
|
||||
*/
|
||||
public void startPeriodic(double period) {
|
||||
m_process.start(period, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop timer events from occuring. Stop any repeating timer events from occuring. This will also
|
||||
* remove any single notification events from the queue. If a timer-based call to the registered
|
||||
* handler is in progress, this function will block until the handler call is complete.
|
||||
*/
|
||||
public void stop() {
|
||||
m_process.stop();
|
||||
}
|
||||
}
|
||||
766
wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDController.java
Normal file
766
wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDController.java
Normal file
@@ -0,0 +1,766 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
import edu.wpi.first.wpilibj.util.BoundaryException;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Class implements a PID Control Loop.
|
||||
*
|
||||
* <p>Creates a separate thread which reads the given PIDSource and takes care of the integral
|
||||
* calculations, as well as writing the given PIDOutput.
|
||||
*
|
||||
* <p>This feedback controller runs in discrete time, so time deltas are not used in the integral
|
||||
* and derivative calculations. Therefore, the sample rate affects the controller's behavior for a
|
||||
* given set of PID constants.
|
||||
*/
|
||||
public class PIDController implements PIDInterface, LiveWindowSendable, Controller {
|
||||
|
||||
public static final double kDefaultPeriod = .05;
|
||||
private static int instances = 0;
|
||||
@SuppressWarnings("MemberName")
|
||||
private double m_P; // factor for "proportional" control
|
||||
@SuppressWarnings("MemberName")
|
||||
private double m_I; // factor for "integral" control
|
||||
@SuppressWarnings("MemberName")
|
||||
private double m_D; // factor for "derivative" control
|
||||
@SuppressWarnings("MemberName")
|
||||
private double m_F; // factor for feedforward term
|
||||
private double m_maximumOutput = 1.0; // |maximum output|
|
||||
private double m_minimumOutput = -1.0; // |minimum output|
|
||||
private double m_maximumInput = 0.0; // maximum input - limit setpoint to this
|
||||
private double m_minimumInput = 0.0; // minimum input - limit setpoint to this
|
||||
// do the endpoints wrap around? eg. Absolute encoder
|
||||
private boolean m_continuous = false;
|
||||
private boolean m_enabled = false; // is the pid controller enabled
|
||||
// the prior error (used to compute velocity)
|
||||
private double m_prevError = 0.0;
|
||||
// the sum of the errors for use in the integral calc
|
||||
private double m_totalError = 0.0;
|
||||
// the tolerance object used to check if on target
|
||||
private Tolerance m_tolerance;
|
||||
private int m_bufLength = 1;
|
||||
private Queue<Double> m_buf;
|
||||
private double m_bufTotal = 0.0;
|
||||
private double m_setpoint = 0.0;
|
||||
private double m_prevSetpoint = 0.0;
|
||||
private double m_error = 0.0;
|
||||
private double m_result = 0.0;
|
||||
private double m_period = kDefaultPeriod;
|
||||
protected PIDSource m_pidInput;
|
||||
protected PIDOutput m_pidOutput;
|
||||
java.util.Timer m_controlLoop;
|
||||
Timer m_setpointTimer;
|
||||
|
||||
/**
|
||||
* Tolerance is the type of tolerance used to specify if the PID controller is on target.
|
||||
*
|
||||
* <p>The various implementations of this class such as PercentageTolerance and AbsoluteTolerance
|
||||
* specify types of tolerance specifications to use.
|
||||
*/
|
||||
public interface Tolerance {
|
||||
boolean onTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally for when Tolerance hasn't been set.
|
||||
*/
|
||||
public class NullTolerance implements Tolerance {
|
||||
@Override
|
||||
public boolean onTarget() {
|
||||
throw new RuntimeException("No tolerance value set when calling onTarget().");
|
||||
}
|
||||
}
|
||||
|
||||
public class PercentageTolerance implements Tolerance {
|
||||
private final double m_percentage;
|
||||
|
||||
PercentageTolerance(double value) {
|
||||
m_percentage = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTarget() {
|
||||
return isAvgErrorValid() && Math.abs(getAvgError()) < m_percentage / 100 * (m_maximumInput
|
||||
- m_minimumInput);
|
||||
}
|
||||
}
|
||||
|
||||
public class AbsoluteTolerance implements Tolerance {
|
||||
private final double m_value;
|
||||
|
||||
AbsoluteTolerance(double value) {
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTarget() {
|
||||
return isAvgErrorValid() && Math.abs(getAvgError()) < m_value;
|
||||
}
|
||||
}
|
||||
|
||||
private class PIDTask extends TimerTask {
|
||||
|
||||
private PIDController m_controller;
|
||||
|
||||
public PIDTask(PIDController controller) {
|
||||
requireNonNull(controller, "Given PIDController was null");
|
||||
|
||||
m_controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
m_controller.calculate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a PID object with the given constants for P, I, D, and F.
|
||||
*
|
||||
* @param Kp the proportional coefficient
|
||||
* @param Ki the integral coefficient
|
||||
* @param Kd the derivative coefficient
|
||||
* @param Kf the feed forward term
|
||||
* @param source The PIDSource object that is used to get values
|
||||
* @param output The PIDOutput object that is set to the output percentage
|
||||
* @param period the loop time for doing calculations. This particularly effects calculations of
|
||||
* the integral and differential terms. The default is 50ms.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public PIDController(double Kp, double Ki, double Kd, double Kf, PIDSource source,
|
||||
PIDOutput output, double period) {
|
||||
requireNonNull(source, "Null PIDSource was given");
|
||||
requireNonNull(output, "Null PIDOutput was given");
|
||||
|
||||
m_controlLoop = new java.util.Timer();
|
||||
m_setpointTimer = new Timer();
|
||||
m_setpointTimer.start();
|
||||
|
||||
m_P = Kp;
|
||||
m_I = Ki;
|
||||
m_D = Kd;
|
||||
m_F = Kf;
|
||||
|
||||
m_pidInput = source;
|
||||
m_pidOutput = output;
|
||||
m_period = period;
|
||||
|
||||
m_controlLoop.schedule(new PIDTask(this), 0L, (long) (m_period * 1000));
|
||||
|
||||
instances++;
|
||||
HLUsageReporting.reportPIDController(instances);
|
||||
m_tolerance = new NullTolerance();
|
||||
|
||||
m_buf = new ArrayDeque<Double>(m_bufLength + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a PID object with the given constants for P, I, D and period.
|
||||
*
|
||||
* @param Kp the proportional coefficient
|
||||
* @param Ki the integral coefficient
|
||||
* @param Kd the derivative coefficient
|
||||
* @param source the PIDSource object that is used to get values
|
||||
* @param output the PIDOutput object that is set to the output percentage
|
||||
* @param period the loop time for doing calculations. This particularly effects calculations of
|
||||
* the integral and differential terms. The default is 50ms.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public PIDController(double Kp, double Ki, double Kd, PIDSource source, PIDOutput output,
|
||||
double period) {
|
||||
this(Kp, Ki, Kd, 0.0, source, output, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a PID object with the given constants for P, I, D, using a 50ms period.
|
||||
*
|
||||
* @param Kp the proportional coefficient
|
||||
* @param Ki the integral coefficient
|
||||
* @param Kd the derivative coefficient
|
||||
* @param source The PIDSource object that is used to get values
|
||||
* @param output The PIDOutput object that is set to the output percentage
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public PIDController(double Kp, double Ki, double Kd, PIDSource source, PIDOutput output) {
|
||||
this(Kp, Ki, Kd, source, output, kDefaultPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a PID object with the given constants for P, I, D, using a 50ms period.
|
||||
*
|
||||
* @param Kp the proportional coefficient
|
||||
* @param Ki the integral coefficient
|
||||
* @param Kd the derivative coefficient
|
||||
* @param Kf the feed forward term
|
||||
* @param source The PIDSource object that is used to get values
|
||||
* @param output The PIDOutput object that is set to the output percentage
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public PIDController(double Kp, double Ki, double Kd, double Kf, PIDSource source,
|
||||
PIDOutput output) {
|
||||
this(Kp, Ki, Kd, Kf, source, output, kDefaultPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the PID object.
|
||||
*/
|
||||
public void free() {
|
||||
m_controlLoop.cancel();
|
||||
synchronized (this) {
|
||||
m_pidOutput = null;
|
||||
m_pidInput = null;
|
||||
m_controlLoop = null;
|
||||
}
|
||||
if (m_table != null) {
|
||||
m_table.removeTableListener(m_listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the input, calculate the output accordingly, and write to the output. This should only be
|
||||
* called by the PIDTask and is created during initialization.
|
||||
*/
|
||||
protected void calculate() {
|
||||
boolean enabled;
|
||||
PIDSource pidInput;
|
||||
|
||||
synchronized (this) {
|
||||
if (m_pidInput == null) {
|
||||
return;
|
||||
}
|
||||
if (m_pidOutput == null) {
|
||||
return;
|
||||
}
|
||||
enabled = m_enabled; // take snapshot of these values...
|
||||
pidInput = m_pidInput;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
double input;
|
||||
double result;
|
||||
final PIDOutput pidOutput;
|
||||
synchronized (this) {
|
||||
input = pidInput.pidGet();
|
||||
}
|
||||
synchronized (this) {
|
||||
m_error = getContinuousError(m_setpoint - input);
|
||||
|
||||
if (m_pidInput.getPIDSourceType().equals(PIDSourceType.kRate)) {
|
||||
if (m_P != 0) {
|
||||
double potentialPGain = (m_totalError + m_error) * m_P;
|
||||
if (potentialPGain < m_maximumOutput) {
|
||||
if (potentialPGain > m_minimumOutput) {
|
||||
m_totalError += m_error;
|
||||
} else {
|
||||
m_totalError = m_minimumOutput / m_P;
|
||||
}
|
||||
} else {
|
||||
m_totalError = m_maximumOutput / m_P;
|
||||
}
|
||||
|
||||
m_result = m_P * m_totalError + m_D * m_error
|
||||
+ calculateFeedForward();
|
||||
}
|
||||
} else {
|
||||
if (m_I != 0) {
|
||||
double potentialIGain = (m_totalError + m_error) * m_I;
|
||||
if (potentialIGain < m_maximumOutput) {
|
||||
if (potentialIGain > m_minimumOutput) {
|
||||
m_totalError += m_error;
|
||||
} else {
|
||||
m_totalError = m_minimumOutput / m_I;
|
||||
}
|
||||
} else {
|
||||
m_totalError = m_maximumOutput / m_I;
|
||||
}
|
||||
}
|
||||
|
||||
m_result = m_P * m_error + m_I * m_totalError
|
||||
+ m_D * (m_error - m_prevError) + calculateFeedForward();
|
||||
}
|
||||
m_prevError = m_error;
|
||||
|
||||
if (m_result > m_maximumOutput) {
|
||||
m_result = m_maximumOutput;
|
||||
} else if (m_result < m_minimumOutput) {
|
||||
m_result = m_minimumOutput;
|
||||
}
|
||||
pidOutput = m_pidOutput;
|
||||
result = m_result;
|
||||
|
||||
// Update the buffer.
|
||||
m_buf.add(m_error);
|
||||
m_bufTotal += m_error;
|
||||
// Remove old elements when the buffer is full.
|
||||
if (m_buf.size() > m_bufLength) {
|
||||
m_bufTotal -= m_buf.remove();
|
||||
}
|
||||
}
|
||||
|
||||
pidOutput.pidWrite(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the feed forward term.
|
||||
*
|
||||
* <p>Both of the provided feed forward calculations are velocity feed forwards. If a different
|
||||
* feed forward calculation is desired, the user can override this function and provide his or
|
||||
* her own. This function does no synchronization because the PIDController class only calls it
|
||||
* in synchronized code, so be careful if calling it oneself.
|
||||
*
|
||||
* <p>If a velocity PID controller is being used, the F term should be set to 1 over the maximum
|
||||
* setpoint for the output. If a position PID controller is being used, the F term should be set
|
||||
* to 1 over the maximum speed for the output measured in setpoint units per this controller's
|
||||
* update period (see the default period in this class's constructor).
|
||||
*/
|
||||
protected double calculateFeedForward() {
|
||||
if (m_pidInput.getPIDSourceType().equals(PIDSourceType.kRate)) {
|
||||
return m_F * getSetpoint();
|
||||
} else {
|
||||
double temp = m_F * getDeltaSetpoint();
|
||||
m_prevSetpoint = m_setpoint;
|
||||
m_setpointTimer.reset();
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PID Controller gain parameters. Set the proportional, integral, and differential
|
||||
* coefficients.
|
||||
*
|
||||
* @param p Proportional coefficient
|
||||
* @param i Integral coefficient
|
||||
* @param d Differential coefficient
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public synchronized void setPID(double p, double i, double d) {
|
||||
m_P = p;
|
||||
m_I = i;
|
||||
m_D = d;
|
||||
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("p", p);
|
||||
m_table.putNumber("i", i);
|
||||
m_table.putNumber("d", d);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PID Controller gain parameters. Set the proportional, integral, and differential
|
||||
* coefficients.
|
||||
*
|
||||
* @param p Proportional coefficient
|
||||
* @param i Integral coefficient
|
||||
* @param d Differential coefficient
|
||||
* @param f Feed forward coefficient
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public synchronized void setPID(double p, double i, double d, double f) {
|
||||
m_P = p;
|
||||
m_I = i;
|
||||
m_D = d;
|
||||
m_F = f;
|
||||
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("p", p);
|
||||
m_table.putNumber("i", i);
|
||||
m_table.putNumber("d", d);
|
||||
m_table.putNumber("f", f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Proportional coefficient.
|
||||
*
|
||||
* @return proportional coefficient
|
||||
*/
|
||||
public synchronized double getP() {
|
||||
return m_P;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Integral coefficient.
|
||||
*
|
||||
* @return integral coefficient
|
||||
*/
|
||||
public synchronized double getI() {
|
||||
return m_I;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Differential coefficient.
|
||||
*
|
||||
* @return differential coefficient
|
||||
*/
|
||||
public synchronized double getD() {
|
||||
return m_D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Feed forward coefficient.
|
||||
*
|
||||
* @return feed forward coefficient
|
||||
*/
|
||||
public synchronized double getF() {
|
||||
return m_F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current PID result This is always centered on zero and constrained the the max and
|
||||
* min outs.
|
||||
*
|
||||
* @return the latest calculated output
|
||||
*/
|
||||
public synchronized double get() {
|
||||
return m_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PID controller to consider the input to be continuous, Rather then using the max and
|
||||
* min in as constraints, it considers them to be the same point and automatically calculates the
|
||||
* shortest route to the setpoint.
|
||||
*
|
||||
* @param continuous Set to true turns on continuous, false turns off continuous
|
||||
*/
|
||||
public synchronized void setContinuous(boolean continuous) {
|
||||
m_continuous = continuous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PID controller to consider the input to be continuous, Rather then using the max and
|
||||
* min in as constraints, it considers them to be the same point and automatically calculates the
|
||||
* shortest route to the setpoint.
|
||||
*/
|
||||
public synchronized void setContinuous() {
|
||||
setContinuous(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum and minimum values expected from the input and setpoint.
|
||||
*
|
||||
* @param minimumInput the minimum value expected from the input
|
||||
* @param maximumInput the maximum value expected from the input
|
||||
*/
|
||||
public synchronized void setInputRange(double minimumInput, double maximumInput) {
|
||||
if (minimumInput > maximumInput) {
|
||||
throw new BoundaryException("Lower bound is greater than upper bound");
|
||||
}
|
||||
m_minimumInput = minimumInput;
|
||||
m_maximumInput = maximumInput;
|
||||
setSetpoint(m_setpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum and maximum values to write.
|
||||
*
|
||||
* @param minimumOutput the minimum percentage to write to the output
|
||||
* @param maximumOutput the maximum percentage to write to the output
|
||||
*/
|
||||
public synchronized void setOutputRange(double minimumOutput, double maximumOutput) {
|
||||
if (minimumOutput > maximumOutput) {
|
||||
throw new BoundaryException("Lower bound is greater than upper bound");
|
||||
}
|
||||
m_minimumOutput = minimumOutput;
|
||||
m_maximumOutput = maximumOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the setpoint for the PIDController Clears the queue for GetAvgError().
|
||||
*
|
||||
* @param setpoint the desired setpoint
|
||||
*/
|
||||
public synchronized void setSetpoint(double setpoint) {
|
||||
if (m_maximumInput > m_minimumInput) {
|
||||
if (setpoint > m_maximumInput) {
|
||||
m_setpoint = m_maximumInput;
|
||||
} else if (setpoint < m_minimumInput) {
|
||||
m_setpoint = m_minimumInput;
|
||||
} else {
|
||||
m_setpoint = setpoint;
|
||||
}
|
||||
} else {
|
||||
m_setpoint = setpoint;
|
||||
}
|
||||
|
||||
m_buf.clear();
|
||||
m_bufTotal = 0;
|
||||
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("setpoint", m_setpoint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current setpoint of the PIDController.
|
||||
*
|
||||
* @return the current setpoint
|
||||
*/
|
||||
public synchronized double getSetpoint() {
|
||||
return m_setpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the change in setpoint over time of the PIDController.
|
||||
*
|
||||
* @return the change in setpoint over time
|
||||
*/
|
||||
public synchronized double getDeltaSetpoint() {
|
||||
return (m_setpoint - m_prevSetpoint) / m_setpointTimer.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current difference of the input from the setpoint.
|
||||
*
|
||||
* @return the current error
|
||||
*/
|
||||
public synchronized double getError() {
|
||||
return getContinuousError(getSetpoint() - m_pidInput.pidGet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets what type of input the PID controller will use.
|
||||
*
|
||||
* @param pidSource the type of input
|
||||
*/
|
||||
void setPIDSourceType(PIDSourceType pidSource) {
|
||||
m_pidInput.setPIDSourceType(pidSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of input the PID controller is using.
|
||||
*
|
||||
* @return the PID controller input type
|
||||
*/
|
||||
PIDSourceType getPIDSourceType() {
|
||||
return m_pidInput.getPIDSourceType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current difference of the error over the past few iterations. You can specify the
|
||||
* number of iterations to average with setToleranceBuffer() (defaults to 1). getAvgError() is
|
||||
* used for the onTarget() function.
|
||||
*
|
||||
* @return the current average of the error
|
||||
*/
|
||||
public synchronized double getAvgError() {
|
||||
double avgError = 0;
|
||||
// Don't divide by zero.
|
||||
if (m_buf.size() != 0) {
|
||||
avgError = m_bufTotal / m_buf.size();
|
||||
}
|
||||
return avgError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not any values have been collected. If no values have been collected,
|
||||
* getAvgError is 0, which is invalid.
|
||||
*
|
||||
* @return True if {@link #getAvgError()} is currently valid.
|
||||
*/
|
||||
private synchronized boolean isAvgErrorValid() {
|
||||
return m_buf.size() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PID tolerance using a Tolerance object. Tolerance can be specified as a percentage of
|
||||
* the range or as an absolute value. The Tolerance object encapsulates those options in an
|
||||
* object. Use it by creating the type of tolerance that you want to use: setTolerance(new
|
||||
* PIDController.AbsoluteTolerance(0.1))
|
||||
*
|
||||
* @param tolerance a tolerance object of the right type, e.g. PercentTolerance or
|
||||
* AbsoluteTolerance
|
||||
*/
|
||||
public void setTolerance(Tolerance tolerance) {
|
||||
m_tolerance = tolerance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the absolute error which is considered tolerable for use with OnTarget.
|
||||
*
|
||||
* @param absvalue absolute error which is tolerable in the units of the input object
|
||||
*/
|
||||
public synchronized void setAbsoluteTolerance(double absvalue) {
|
||||
m_tolerance = new AbsoluteTolerance(absvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the percentage error which is considered tolerable for use with OnTarget. (Input of 15.0 =
|
||||
* 15 percent)
|
||||
*
|
||||
* @param percentage percent error which is tolerable
|
||||
*/
|
||||
public synchronized void setPercentTolerance(double percentage) {
|
||||
m_tolerance = new PercentageTolerance(percentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of previous error samples to average for tolerancing. When determining whether a
|
||||
* mechanism is on target, the user may want to use a rolling average of previous measurements
|
||||
* instead of a precise position or velocity. This is useful for noisy sensors which return a few
|
||||
* erroneous measurements when the mechanism is on target. However, the mechanism will not
|
||||
* register as on target for at least the specified bufLength cycles.
|
||||
*
|
||||
* @param bufLength Number of previous cycles to average.
|
||||
*/
|
||||
public synchronized void setToleranceBuffer(int bufLength) {
|
||||
m_bufLength = bufLength;
|
||||
|
||||
// Cut the existing buffer down to size if needed.
|
||||
while (m_buf.size() > bufLength) {
|
||||
m_bufTotal -= m_buf.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the error is within the percentage of the total input range, determined by
|
||||
* setTolerance. This assumes that the maximum and minimum input were set using setInput.
|
||||
*
|
||||
* @return true if the error is less than the tolerance
|
||||
*/
|
||||
public synchronized boolean onTarget() {
|
||||
return m_tolerance.onTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin running the PIDController.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void enable() {
|
||||
m_enabled = true;
|
||||
|
||||
if (m_table != null) {
|
||||
m_table.putBoolean("enabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop running the PIDController, this sets the output to zero before stopping.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void disable() {
|
||||
m_pidOutput.pidWrite(0);
|
||||
m_enabled = false;
|
||||
|
||||
if (m_table != null) {
|
||||
m_table.putBoolean("enabled", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if PIDController is enabled.
|
||||
*/
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the previous error,, the integral term, and disable the controller.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
disable();
|
||||
m_prevError = 0;
|
||||
m_totalError = 0;
|
||||
m_result = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "PIDController";
|
||||
}
|
||||
|
||||
private final ITableListener m_listener = (table, key, value, isNew) -> {
|
||||
if (key.equals("p") || key.equals("i") || key.equals("d") || key.equals("f")) {
|
||||
if (getP() != table.getNumber("p", 0.0) || getI() != table.getNumber("i", 0.0)
|
||||
|| getD() != table.getNumber("d", 0.0) || getF() != table.getNumber("f", 0.0)) {
|
||||
setPID(table.getNumber("p", 0.0), table.getNumber("i", 0.0), table.getNumber("d", 0.0),
|
||||
table.getNumber("f", 0.0));
|
||||
}
|
||||
} else if (key.equals("setpoint")) {
|
||||
if (getSetpoint() != (Double) value) {
|
||||
setSetpoint((Double) value);
|
||||
}
|
||||
} else if (key.equals("enabled") && isEnabled() != (Boolean) value) {
|
||||
if ((Boolean) value) {
|
||||
enable();
|
||||
} else {
|
||||
disable();
|
||||
}
|
||||
}
|
||||
};
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable table) {
|
||||
if (this.m_table != null) {
|
||||
m_table.removeTableListener(m_listener);
|
||||
}
|
||||
m_table = table;
|
||||
if (table != null) {
|
||||
table.putNumber("p", getP());
|
||||
table.putNumber("i", getI());
|
||||
table.putNumber("d", getD());
|
||||
table.putNumber("f", getF());
|
||||
table.putNumber("setpoint", getSetpoint());
|
||||
table.putBoolean("enabled", isEnabled());
|
||||
table.addTableListener(m_listener, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps error around for continuous inputs. The original error is returned if continuous mode is
|
||||
* disabled. This is an unsynchronized function.
|
||||
*
|
||||
* @param error The current error of the PID controller.
|
||||
* @return Error for continuous inputs.
|
||||
*/
|
||||
protected double getContinuousError(double error) {
|
||||
if (m_continuous && Math.abs(error) > (m_maximumInput - m_minimumInput) / 2) {
|
||||
if (error > 0) {
|
||||
return error - (m_maximumInput - m_minimumInput);
|
||||
} else {
|
||||
return error + (m_maximumInput - m_minimumInput);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
disable();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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;
|
||||
|
||||
@SuppressWarnings("SummaryJavadoc")
|
||||
public interface PIDInterface extends Controller {
|
||||
|
||||
@SuppressWarnings("ParameterName")
|
||||
void setPID(double p, double i, double d);
|
||||
|
||||
double getP();
|
||||
|
||||
double getI();
|
||||
|
||||
double getD();
|
||||
|
||||
void setSetpoint(double setpoint);
|
||||
|
||||
double getSetpoint();
|
||||
|
||||
double getError();
|
||||
|
||||
void enable();
|
||||
|
||||
void disable();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
void reset();
|
||||
}
|
||||
22
wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDOutput.java
Normal file
22
wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDOutput.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* This interface allows PIDController to write it's results to its output.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PIDOutput {
|
||||
|
||||
/**
|
||||
* Set the output to the value calculated by PIDController.
|
||||
*
|
||||
* @param output the value calculated by PIDController
|
||||
*/
|
||||
void pidWrite(double output);
|
||||
}
|
||||
34
wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDSource.java
Normal file
34
wpilibj/src/main/java/edu/wpi/first/wpilibj/PIDSource.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* This interface allows for PIDController to automatically read from this object.
|
||||
*/
|
||||
public interface PIDSource {
|
||||
/**
|
||||
* Set which parameter of the device you are using as a process control variable.
|
||||
*
|
||||
* @param pidSource An enum to select the parameter.
|
||||
*/
|
||||
void setPIDSourceType(PIDSourceType pidSource);
|
||||
|
||||
/**
|
||||
* Get which parameter of the device you are using as a process control variable.
|
||||
*
|
||||
* @return the currently selected PID source parameter
|
||||
*/
|
||||
PIDSourceType getPIDSourceType();
|
||||
|
||||
/**
|
||||
* Get the result to use in PIDController.
|
||||
*
|
||||
* @return the result to use in PIDController
|
||||
*/
|
||||
double pidGet();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2017. 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;
|
||||
|
||||
/**
|
||||
* A description for the type of output value to provide to a PIDController.
|
||||
*/
|
||||
public enum PIDSourceType {
|
||||
kDisplacement,
|
||||
kRate
|
||||
}
|
||||
286
wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java
Normal file
286
wpilibj/src/main/java/edu/wpi/first/wpilibj/PWM.java
Normal file
@@ -0,0 +1,286 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.DIOJNI;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.hal.PWMJNI;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
/**
|
||||
* Class implements the PWM generation in the FPGA.
|
||||
*
|
||||
* <p>The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They are mapped to
|
||||
* the hardware dependent values, in this case 0-2000 for the FPGA. Changes are immediately sent to
|
||||
* the FPGA, and the update occurs at the next FPGA cycle. There is no delay.
|
||||
*
|
||||
* <p>As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as follows: - 2000 =
|
||||
* maximum pulse width - 1999 to 1001 = linear scaling from "full forward" to "center" - 1000 =
|
||||
* center value - 999 to 2 = linear scaling from "center" to "full reverse" - 1 = minimum pulse
|
||||
* width (currently .5ms) - 0 = disabled (i.e. PWM output is held low)
|
||||
*/
|
||||
public class PWM extends SensorBase implements LiveWindowSendable {
|
||||
/**
|
||||
* Represents the amount to multiply the minimum servo-pulse pwm period by.
|
||||
*/
|
||||
public enum PeriodMultiplier {
|
||||
/**
|
||||
* Period Multiplier: don't skip pulses.
|
||||
*/
|
||||
k1X,
|
||||
/**
|
||||
* Period Multiplier: skip every other pulse.
|
||||
*/
|
||||
k2X,
|
||||
/**
|
||||
* Period Multiplier: skip three out of four pulses.
|
||||
*/
|
||||
k4X
|
||||
}
|
||||
|
||||
private int m_channel;
|
||||
private int m_handle;
|
||||
|
||||
/**
|
||||
* Allocate a PWM given a channel.
|
||||
*
|
||||
* @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port
|
||||
*/
|
||||
public PWM(final int channel) {
|
||||
checkPWMChannel(channel);
|
||||
m_channel = channel;
|
||||
|
||||
m_handle = PWMJNI.initializePWMPort(DIOJNI.getPort((byte) channel));
|
||||
|
||||
setDisabled();
|
||||
|
||||
PWMJNI.setPWMEliminateDeadband(m_handle, false);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_PWM, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the PWM channel.
|
||||
*
|
||||
* <p>Free the resource associated with the PWM channel and set the value to 0.
|
||||
*/
|
||||
public void free() {
|
||||
if (m_handle == 0) {
|
||||
return;
|
||||
}
|
||||
setDisabled();
|
||||
PWMJNI.freePWMPort(m_handle);
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally eliminate the deadband from a speed controller.
|
||||
*
|
||||
* @param eliminateDeadband If true, set the motor curve on the Jaguar to eliminate the deadband
|
||||
* in the middle of the range. Otherwise, keep the full range without
|
||||
* modifying any values.
|
||||
*/
|
||||
public void enableDeadbandElimination(boolean eliminateDeadband) {
|
||||
PWMJNI.setPWMEliminateDeadband(m_handle, eliminateDeadband);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bounds on the PWM pulse widths. This sets the bounds on the PWM values for a particular
|
||||
* type of controller. The values determine the upper and lower speeds as well as the deadband
|
||||
* bracket.
|
||||
*
|
||||
* @param max The max PWM pulse width in ms
|
||||
* @param deadbandMax The high end of the deadband range pulse width in ms
|
||||
* @param center The center (off) pulse width in ms
|
||||
* @param deadbandMin The low end of the deadband pulse width in ms
|
||||
* @param min The minimum pulse width in ms
|
||||
*/
|
||||
public void setBounds(double max, double deadbandMax, double center, double deadbandMin,
|
||||
double min) {
|
||||
PWMJNI.setPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bounds on the PWM pulse widths. This Gets the bounds on the PWM values for a
|
||||
* particular type of controller. The values determine the upper and lower speeds as well
|
||||
* as the deadband bracket.
|
||||
*/
|
||||
public PWMConfigDataResult getRawBounds() {
|
||||
return PWMJNI.getPWMConfigRaw(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the channel number associated with the PWM Object.
|
||||
*
|
||||
* @return The channel number.
|
||||
*/
|
||||
public int getChannel() {
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PWM value based on a position.
|
||||
*
|
||||
* <p>This is intended to be used by servos.
|
||||
*
|
||||
* @param pos The position to set the servo between 0.0 and 1.0.
|
||||
* @pre SetMaxPositivePwm() called.
|
||||
* @pre SetMinNegativePwm() called.
|
||||
*/
|
||||
public void setPosition(double pos) {
|
||||
PWMJNI.setPWMPosition(m_handle, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PWM value in terms of a position.
|
||||
*
|
||||
* <p>This is intended to be used by servos.
|
||||
*
|
||||
* @return The position the servo is set to between 0.0 and 1.0.
|
||||
* @pre SetMaxPositivePwm() called.
|
||||
* @pre SetMinNegativePwm() called.
|
||||
*/
|
||||
public double getPosition() {
|
||||
return PWMJNI.getPWMPosition(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PWM value based on a speed.
|
||||
*
|
||||
* <p>This is intended to be used by speed controllers.
|
||||
*
|
||||
* @param speed The speed to set the speed controller between -1.0 and 1.0.
|
||||
* @pre SetMaxPositivePwm() called.
|
||||
* @pre SetMinPositivePwm() called.
|
||||
* @pre SetCenterPwm() called.
|
||||
* @pre SetMaxNegativePwm() called.
|
||||
* @pre SetMinNegativePwm() called.
|
||||
*/
|
||||
public void setSpeed(double speed) {
|
||||
PWMJNI.setPWMSpeed(m_handle, speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PWM value in terms of speed.
|
||||
*
|
||||
* <p>This is intended to be used by speed controllers.
|
||||
*
|
||||
* @return The most recently set speed between -1.0 and 1.0.
|
||||
* @pre SetMaxPositivePwm() called.
|
||||
* @pre SetMinPositivePwm() called.
|
||||
* @pre SetMaxNegativePwm() called.
|
||||
* @pre SetMinNegativePwm() called.
|
||||
*/
|
||||
public double getSpeed() {
|
||||
return PWMJNI.getPWMSpeed(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PWM value directly to the hardware.
|
||||
*
|
||||
* <p>Write a raw value to a PWM channel.
|
||||
*
|
||||
* @param value Raw PWM value. Range 0 - 255.
|
||||
*/
|
||||
public void setRaw(int value) {
|
||||
PWMJNI.setPWMRaw(m_handle, (short) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PWM value directly from the hardware.
|
||||
*
|
||||
* <p>Read a raw value from a PWM channel.
|
||||
*
|
||||
* @return Raw PWM control value. Range: 0 - 255.
|
||||
*/
|
||||
public int getRaw() {
|
||||
return PWMJNI.getPWMRaw(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily disables the PWM output. The next set call will reenable
|
||||
* the output.
|
||||
*/
|
||||
public void setDisabled() {
|
||||
PWMJNI.setPWMDisabled(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Slow down the PWM signal for old devices.
|
||||
*
|
||||
* @param mult The period multiplier to apply to this channel
|
||||
*/
|
||||
public void setPeriodMultiplier(PeriodMultiplier mult) {
|
||||
switch (mult) {
|
||||
case k4X:
|
||||
// Squelch 3 out of 4 outputs
|
||||
PWMJNI.setPWMPeriodScale(m_handle, 3);
|
||||
break;
|
||||
case k2X:
|
||||
// Squelch 1 out of 2 outputs
|
||||
PWMJNI.setPWMPeriodScale(m_handle, 1);
|
||||
break;
|
||||
case k1X:
|
||||
// Don't squelch any outputs
|
||||
PWMJNI.setPWMPeriodScale(m_handle, 0);
|
||||
break;
|
||||
default:
|
||||
// Cannot hit this, limited by PeriodMultiplier enum
|
||||
}
|
||||
}
|
||||
|
||||
protected void setZeroLatch() {
|
||||
PWMJNI.latchPWMZero(m_handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Speed Controller";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
private ITableListener m_tableListener;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", getSpeed());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
setSpeed(0); // Stop for safety
|
||||
m_tableListener = (source, key, value, isNew) -> setSpeed((double) value);
|
||||
m_table.addTableListener("Value", m_tableListener, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
setSpeed(0); // Stop for safety
|
||||
// TODO: Broken, should only remove the listener from "Value" only.
|
||||
m_table.removeTableListener(m_tableListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* Structure for holding the config data result for PWM.
|
||||
*/
|
||||
public class PWMConfigDataResult {
|
||||
|
||||
PWMConfigDataResult(int max, int deadbandMax, int center, int deadbandMin, int min) {
|
||||
this.max = max;
|
||||
this.deadbandMax = deadbandMax;
|
||||
this.center = center;
|
||||
this.deadbandMin = deadbandMin;
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum PWM value.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public int max;
|
||||
|
||||
/**
|
||||
* The deadband maximum PWM value.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public int deadbandMax;
|
||||
|
||||
/**
|
||||
* The center PWM value.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public int center;
|
||||
|
||||
/**
|
||||
* The deadband minimum PWM value.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public int deadbandMin;
|
||||
|
||||
/**
|
||||
* The minimum PWM value.
|
||||
*/
|
||||
@SuppressWarnings("MemberName")
|
||||
public int min;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* Common base class for all PWM Speed Controllers.
|
||||
*/
|
||||
public abstract class PWMSpeedController extends SafePWM implements SpeedController {
|
||||
private boolean m_isInverted = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param channel The PWM channel that the controller is attached to. 0-9 are on-board, 10-19 are
|
||||
* on the MXP port
|
||||
*/
|
||||
protected PWMSpeedController(int channel) {
|
||||
super(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PWM value.
|
||||
*
|
||||
* <p>The PWM value is set using a range of -1.0 to 1.0, appropriately scaling the value for the
|
||||
* FPGA.
|
||||
*
|
||||
* @param speed The speed value between -1.0 and 1.0 to set.
|
||||
*/
|
||||
@Override
|
||||
public void set(double speed) {
|
||||
setSpeed(m_isInverted ? -speed : speed);
|
||||
feed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recently set value of the PWM.
|
||||
*
|
||||
* @return The most recently set value for the PWM between -1.0 and 1.0.
|
||||
*/
|
||||
@Override
|
||||
public double get() {
|
||||
return getSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInverted(boolean isInverted) {
|
||||
m_isInverted = isInverted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getInverted() {
|
||||
return m_isInverted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the PID value as seen in the PIDOutput base object.
|
||||
*
|
||||
* @param output Write out the PWM value as was found in the PIDController
|
||||
*/
|
||||
@Override
|
||||
public void pidWrite(double output) {
|
||||
set(output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2014-2017. 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.hal.PDPJNI;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* Class for getting voltage, current, temperature, power and energy from the Power Distribution
|
||||
* Panel over CAN.
|
||||
*/
|
||||
public class PowerDistributionPanel extends SensorBase implements LiveWindowSendable {
|
||||
|
||||
private final int m_module;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param module The CAN ID of the PDP
|
||||
*/
|
||||
public PowerDistributionPanel(int module) {
|
||||
m_module = module;
|
||||
checkPDPModule(module);
|
||||
PDPJNI.initializePDP(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Uses the default CAN ID (0).
|
||||
*/
|
||||
public PowerDistributionPanel() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the input voltage of the PDP.
|
||||
*
|
||||
* @return The voltage of the PDP in volts
|
||||
*/
|
||||
public double getVoltage() {
|
||||
return PDPJNI.getPDPVoltage(m_module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the temperature of the PDP.
|
||||
*
|
||||
* @return The temperature of the PDP in degrees Celsius
|
||||
*/
|
||||
public double getTemperature() {
|
||||
return PDPJNI.getPDPTemperature(m_module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the current of a single channel of the PDP.
|
||||
*
|
||||
* @return The current of one of the PDP channels (channels 0-15) in Amperes
|
||||
*/
|
||||
public double getCurrent(int channel) {
|
||||
double current = PDPJNI.getPDPChannelCurrent((byte) channel, m_module);
|
||||
|
||||
checkPDPChannel(channel);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the current of all monitored PDP channels (0-15).
|
||||
*
|
||||
* @return The current of all the channels in Amperes
|
||||
*/
|
||||
public double getTotalCurrent() {
|
||||
return PDPJNI.getPDPTotalCurrent(m_module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the total power drawn from the monitored PDP channels.
|
||||
*
|
||||
* @return the total power in Watts
|
||||
*/
|
||||
public double getTotalPower() {
|
||||
return PDPJNI.getPDPTotalPower(m_module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the total energy drawn from the monitored PDP channels.
|
||||
*
|
||||
* @return the total energy in Joules
|
||||
*/
|
||||
public double getTotalEnergy() {
|
||||
return PDPJNI.getPDPTotalEnergy(m_module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the total energy to 0.
|
||||
*/
|
||||
public void resetTotalEnergy() {
|
||||
PDPJNI.resetPDPTotalEnergy(m_module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all PDP sticky faults.
|
||||
*/
|
||||
public void clearStickyFaults() {
|
||||
PDPJNI.clearPDPStickyFaults(m_module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "PowerDistributionPanel";
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Chan0", getCurrent(0));
|
||||
m_table.putNumber("Chan1", getCurrent(1));
|
||||
m_table.putNumber("Chan2", getCurrent(2));
|
||||
m_table.putNumber("Chan3", getCurrent(3));
|
||||
m_table.putNumber("Chan4", getCurrent(4));
|
||||
m_table.putNumber("Chan5", getCurrent(5));
|
||||
m_table.putNumber("Chan6", getCurrent(6));
|
||||
m_table.putNumber("Chan7", getCurrent(7));
|
||||
m_table.putNumber("Chan8", getCurrent(8));
|
||||
m_table.putNumber("Chan9", getCurrent(9));
|
||||
m_table.putNumber("Chan10", getCurrent(10));
|
||||
m_table.putNumber("Chan11", getCurrent(11));
|
||||
m_table.putNumber("Chan12", getCurrent(12));
|
||||
m_table.putNumber("Chan13", getCurrent(13));
|
||||
m_table.putNumber("Chan14", getCurrent(14));
|
||||
m_table.putNumber("Chan15", getCurrent(15));
|
||||
m_table.putNumber("Voltage", getVoltage());
|
||||
m_table.putNumber("TotalCurrent", getTotalCurrent());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PDP doesn't have to do anything special when entering the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
/**
|
||||
* PDP doesn't have to do anything special when exiting the LiveWindow. {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
|
||||
}
|
||||
251
wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java
Normal file
251
wpilibj/src/main/java/edu/wpi/first/wpilibj/Preferences.java
Normal file
@@ -0,0 +1,251 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.util.Vector;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* The preferences class provides a relatively simple way to save important values to the roboRIO to
|
||||
* access the next time the roboRIO is booted.
|
||||
*
|
||||
* <p> This class loads and saves from a file inside the roboRIO. The user can not access the file
|
||||
* directly, but may modify values at specific fields which will then be automatically saved to the
|
||||
* file by the NetworkTables server. </p>
|
||||
*
|
||||
* <p> This class is thread safe. </p>
|
||||
*
|
||||
* <p> This will also interact with {@link NetworkTable} by creating a table called "Preferences"
|
||||
* with all the key-value pairs. </p>
|
||||
*/
|
||||
public class Preferences {
|
||||
|
||||
/**
|
||||
* The Preferences table name.
|
||||
*/
|
||||
private static final String TABLE_NAME = "Preferences";
|
||||
/**
|
||||
* The singleton instance.
|
||||
*/
|
||||
private static Preferences instance;
|
||||
/**
|
||||
* The network table.
|
||||
*/
|
||||
private final NetworkTable m_table;
|
||||
/**
|
||||
* Listener to set all Preferences values to persistent (for backwards compatibility with old
|
||||
* dashboards).
|
||||
*/
|
||||
private final ITableListener m_listener = new ITableListener() {
|
||||
@Override
|
||||
public void valueChanged(ITable table, String key, Object value, boolean isNew) {
|
||||
// unused
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueChangedEx(ITable table, String key, Object value, int flags) {
|
||||
table.setPersistent(key);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the preferences instance.
|
||||
*
|
||||
* @return the preferences instance
|
||||
*/
|
||||
public static synchronized Preferences getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new Preferences();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a preference class.
|
||||
*/
|
||||
private Preferences() {
|
||||
m_table = NetworkTable.getTable(TABLE_NAME);
|
||||
m_table.addTableListenerEx(m_listener, ITable.NOTIFY_NEW | ITable.NOTIFY_IMMEDIATE);
|
||||
HAL.report(tResourceType.kResourceType_Preferences, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the vector of keys.
|
||||
* @return a vector of the keys
|
||||
*/
|
||||
public Vector<String> getKeys() {
|
||||
return new Vector<>(m_table.getKeys());
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given string into the preferences table.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws NullPointerException if value is null
|
||||
*/
|
||||
public void putString(String key, String value) {
|
||||
requireNonNull(value, "Provided value was null");
|
||||
|
||||
m_table.putString(key, value);
|
||||
m_table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given int into the preferences table.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public void putInt(String key, int value) {
|
||||
m_table.putNumber(key, value);
|
||||
m_table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given double into the preferences table.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public void putDouble(String key, double value) {
|
||||
m_table.putNumber(key, value);
|
||||
m_table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given float into the preferences table.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public void putFloat(String key, float value) {
|
||||
m_table.putNumber(key, value);
|
||||
m_table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given boolean into the preferences table.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public void putBoolean(String key, boolean value) {
|
||||
m_table.putBoolean(key, value);
|
||||
m_table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given long into the preferences table.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public void putLong(String key, long value) {
|
||||
m_table.putNumber(key, value);
|
||||
m_table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not there is a key with the given name.
|
||||
*
|
||||
* @param key the key
|
||||
* @return if there is a value at the given key
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
return m_table.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a preference.
|
||||
*
|
||||
* @param key the key
|
||||
*/
|
||||
public void remove(String key) {
|
||||
m_table.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string at the given key. If this table does not have a value for that position,
|
||||
* then the given backup value will be returned.
|
||||
*
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
*/
|
||||
public String getString(String key, String backup) {
|
||||
return m_table.getString(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the int at the given key. If this table does not have a value for that position, then
|
||||
* the given backup value will be returned.
|
||||
*
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
*/
|
||||
public int getInt(String key, int backup) {
|
||||
return (int) m_table.getNumber(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the double at the given key. If this table does not have a value for that position,
|
||||
* then the given backup value will be returned.
|
||||
*
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
*/
|
||||
public double getDouble(String key, double backup) {
|
||||
return m_table.getNumber(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boolean at the given key. If this table does not have a value for that position,
|
||||
* then the given backup value will be returned.
|
||||
*
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
*/
|
||||
public boolean getBoolean(String key, boolean backup) {
|
||||
return m_table.getBoolean(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the float at the given key. If this table does not have a value for that position, then
|
||||
* the given backup value will be returned.
|
||||
*
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
*/
|
||||
public float getFloat(String key, float backup) {
|
||||
return (float) m_table.getNumber(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long at the given key. If this table does not have a value for that position, then
|
||||
* the given backup value will be returned.
|
||||
*
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
*/
|
||||
public long getLong(String key, long backup) {
|
||||
return (long) m_table.getNumber(key, backup);
|
||||
}
|
||||
}
|
||||
372
wpilibj/src/main/java/edu/wpi/first/wpilibj/Relay.java
Normal file
372
wpilibj/src/main/java/edu/wpi/first/wpilibj/Relay.java
Normal file
@@ -0,0 +1,372 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.hal.RelayJNI;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* 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 channels 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 MotorSafety, LiveWindowSendable {
|
||||
private MotorSafetyHelper m_safetyHelper;
|
||||
|
||||
/**
|
||||
* 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 enum Value {
|
||||
kOff("Off"),
|
||||
kOn("On"),
|
||||
kForward("Forward"),
|
||||
kReverse("Reverse");
|
||||
|
||||
private final String m_prettyValue;
|
||||
|
||||
Value(String prettyValue) {
|
||||
m_prettyValue = prettyValue;
|
||||
}
|
||||
|
||||
public String getPrettyValue() {
|
||||
return m_prettyValue;
|
||||
}
|
||||
|
||||
public static Optional<Value> getValueOf(String value) {
|
||||
return Arrays.stream(Value.values()).filter(v -> v.m_prettyValue.equals(value)).findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Direction(s) that a relay is configured to operate in.
|
||||
*/
|
||||
public enum Direction {
|
||||
/**
|
||||
* direction: both directions are valid.
|
||||
*/
|
||||
|
||||
kBoth,
|
||||
/**
|
||||
* direction: Only forward is valid.
|
||||
*/
|
||||
kForward,
|
||||
/**
|
||||
* direction: only reverse is valid.
|
||||
*/
|
||||
kReverse
|
||||
}
|
||||
|
||||
private final int m_channel;
|
||||
|
||||
private int m_forwardHandle = 0;
|
||||
private int m_reverseHandle = 0;
|
||||
|
||||
private Direction m_direction;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
int portHandle = RelayJNI.getPort((byte) m_channel);
|
||||
if (m_direction == Direction.kBoth || m_direction == Direction.kForward) {
|
||||
m_forwardHandle = RelayJNI.initializeRelayPort(portHandle, true);
|
||||
HAL.report(tResourceType.kResourceType_Relay, m_channel);
|
||||
}
|
||||
if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) {
|
||||
m_reverseHandle = RelayJNI.initializeRelayPort(portHandle, false);
|
||||
HAL.report(tResourceType.kResourceType_Relay, m_channel + 128);
|
||||
}
|
||||
|
||||
m_safetyHelper = new MotorSafetyHelper(this);
|
||||
m_safetyHelper.setSafetyEnabled(false);
|
||||
|
||||
LiveWindow.addActuator("Relay", m_channel, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relay constructor given a channel.
|
||||
*
|
||||
* @param channel The channel number for this relay (0 - 3).
|
||||
* @param direction The direction that the Relay object will control.
|
||||
*/
|
||||
public Relay(final int channel, Direction direction) {
|
||||
m_channel = channel;
|
||||
m_direction = requireNonNull(direction, "Null Direction was given");
|
||||
initRelay();
|
||||
set(Value.kOff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relay constructor given a channel, allowing both directions.
|
||||
*
|
||||
* @param channel The channel number for this relay (0 - 3).
|
||||
*/
|
||||
public Relay(final int channel) {
|
||||
this(channel, Direction.kBoth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
try {
|
||||
RelayJNI.setRelay(m_forwardHandle, false);
|
||||
} catch (RuntimeException ex) {
|
||||
// do nothing. Ignore
|
||||
}
|
||||
try {
|
||||
RelayJNI.setRelay(m_reverseHandle, false);
|
||||
} catch (RuntimeException ex) {
|
||||
// do nothing. Ignore
|
||||
}
|
||||
|
||||
RelayJNI.freeRelayPort(m_forwardHandle);
|
||||
RelayJNI.freeRelayPort(m_reverseHandle);
|
||||
|
||||
m_forwardHandle = 0;
|
||||
m_reverseHandle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the relay state.
|
||||
*
|
||||
* <p>Valid values depend on which directions of the relay are controlled by the object.
|
||||
*
|
||||
* <p>When set to kBothDirections, the relay can be set to any of the four states: 0v-0v, 12v-0v,
|
||||
* 0v-12v, 12v-12v
|
||||
*
|
||||
* <p>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.
|
||||
*/
|
||||
public void set(Value value) {
|
||||
switch (value) {
|
||||
case kOff:
|
||||
if (m_direction == Direction.kBoth || m_direction == Direction.kForward) {
|
||||
RelayJNI.setRelay(m_forwardHandle, false);
|
||||
}
|
||||
if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) {
|
||||
RelayJNI.setRelay(m_reverseHandle, false);
|
||||
}
|
||||
break;
|
||||
case kOn:
|
||||
if (m_direction == Direction.kBoth || m_direction == Direction.kForward) {
|
||||
RelayJNI.setRelay(m_forwardHandle, true);
|
||||
}
|
||||
if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) {
|
||||
RelayJNI.setRelay(m_reverseHandle, true);
|
||||
}
|
||||
break;
|
||||
case kForward:
|
||||
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) {
|
||||
RelayJNI.setRelay(m_forwardHandle, true);
|
||||
}
|
||||
if (m_direction == Direction.kBoth) {
|
||||
RelayJNI.setRelay(m_reverseHandle, false);
|
||||
}
|
||||
break;
|
||||
case kReverse:
|
||||
if (m_direction == Direction.kForward) {
|
||||
throw new InvalidValueException("A relay configured for forward cannot be set to "
|
||||
+ "reverse");
|
||||
}
|
||||
if (m_direction == Direction.kBoth) {
|
||||
RelayJNI.setRelay(m_forwardHandle, false);
|
||||
}
|
||||
if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) {
|
||||
RelayJNI.setRelay(m_reverseHandle, true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Cannot hit this, limited by Value enum
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Relay State.
|
||||
*
|
||||
* <p>Gets the current state of the relay.
|
||||
*
|
||||
* <p>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() {
|
||||
if (m_direction == Direction.kForward) {
|
||||
if (RelayJNI.getRelay(m_forwardHandle)) {
|
||||
return Value.kOn;
|
||||
} else {
|
||||
return Value.kOff;
|
||||
}
|
||||
} else if (m_direction == Direction.kReverse) {
|
||||
if (RelayJNI.getRelay(m_reverseHandle)) {
|
||||
return Value.kOn;
|
||||
} else {
|
||||
return Value.kOff;
|
||||
}
|
||||
} else {
|
||||
if (RelayJNI.getRelay(m_forwardHandle)) {
|
||||
if (RelayJNI.getRelay(m_reverseHandle)) {
|
||||
return Value.kOn;
|
||||
} else {
|
||||
return Value.kForward;
|
||||
}
|
||||
} else {
|
||||
if (RelayJNI.getRelay(m_reverseHandle)) {
|
||||
return Value.kReverse;
|
||||
} else {
|
||||
return Value.kOff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channel number.
|
||||
*
|
||||
* @return The channel number.
|
||||
*/
|
||||
public int getChannel() {
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpiration(double timeout) {
|
||||
m_safetyHelper.setExpiration(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getExpiration() {
|
||||
return m_safetyHelper.getExpiration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return m_safetyHelper.isAlive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopMotor() {
|
||||
set(Value.kOff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSafetyEnabled() {
|
||||
return m_safetyHelper.isSafetyEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSafetyEnabled(boolean enabled) {
|
||||
m_safetyHelper.setSafetyEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Relay ID " + getChannel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Relay Direction.
|
||||
*
|
||||
* <p>Changes which values the relay can be set to depending on which direction is used
|
||||
*
|
||||
* <p>Valid inputs are kBothDirections, kForwardOnly, and kReverseOnly
|
||||
*
|
||||
* @param direction The direction for the relay to operate in
|
||||
*/
|
||||
public void setDirection(Direction direction) {
|
||||
requireNonNull(direction, "Null Direction was given");
|
||||
if (m_direction == direction) {
|
||||
return;
|
||||
}
|
||||
|
||||
free();
|
||||
m_direction = direction;
|
||||
initRelay();
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Relay";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
private ITableListener m_tableListener;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putString("Value", get().getPrettyValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
m_tableListener =
|
||||
(source, key, value, isNew) -> set(Value.getValueOf((String) value).orElse(Value.kOff));
|
||||
m_table.addTableListener("Value", m_tableListener, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
// TODO: Broken, should only remove the listener from "Value" only.
|
||||
m_table.removeTableListener(m_tableListener);
|
||||
}
|
||||
}
|
||||
111
wpilibj/src/main/java/edu/wpi/first/wpilibj/Resource.java
Normal file
111
wpilibj/src/main/java/edu/wpi/first/wpilibj/Resource.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.util.AllocationException;
|
||||
import edu.wpi.first.wpilibj.util.CheckedAllocationException;
|
||||
|
||||
/**
|
||||
* Track resources in the program. The Resource class is a convenient way of keeping track of
|
||||
* allocated arbitrary resources in the program. Resources are just indicies that have an lower and
|
||||
* upper bound that are tracked by this class. In the library they are used for tracking allocation
|
||||
* of hardware channels but this is purely arbitrary. The resource class does not do any actual
|
||||
* allocation, but simply tracks if a given index is currently in use.
|
||||
*
|
||||
* <p><b>WARNING:</b> this should only be statically allocated. When the program loads into memory
|
||||
* all the static constructors are called. At that time a linked list of all the "Resources" is
|
||||
* created. Then when the program actually starts - in the Robot constructor, all resources are
|
||||
* initialized. This ensures that the program is restartable in memory without having to
|
||||
* unload/reload.
|
||||
*/
|
||||
public final class Resource {
|
||||
|
||||
private static Resource resourceList = null;
|
||||
private final boolean[] m_numAllocated;
|
||||
private final int m_size;
|
||||
private final Resource m_nextResource;
|
||||
|
||||
/**
|
||||
* Clears all allocated resources.
|
||||
*/
|
||||
public static void restartProgram() {
|
||||
for (Resource r = Resource.resourceList; r != null; r = r.m_nextResource) {
|
||||
for (int i = 0; i < r.m_size; i++) {
|
||||
r.m_numAllocated[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate storage for a new instance of Resource. Allocate a bool array of values that will get
|
||||
* initialized to indicate that no resources have been allocated yet. The indicies of the
|
||||
* resources are 0..size-1.
|
||||
*
|
||||
* @param size The number of blocks to allocate
|
||||
*/
|
||||
public Resource(final int size) {
|
||||
m_size = size;
|
||||
m_numAllocated = new boolean[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
m_numAllocated[i] = false;
|
||||
}
|
||||
m_nextResource = Resource.resourceList;
|
||||
Resource.resourceList = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a resource. When a resource is requested, mark it allocated. In this case, a free
|
||||
* resource value within the range is located and returned after it is marked allocated.
|
||||
*
|
||||
* @return The index of the allocated block.
|
||||
* @throws CheckedAllocationException If there are no resources available to be allocated.
|
||||
*/
|
||||
public int allocate() throws CheckedAllocationException {
|
||||
for (int i = 0; i < m_size; i++) {
|
||||
if (!m_numAllocated[i]) {
|
||||
m_numAllocated[i] = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new CheckedAllocationException("No available resources");
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a specific resource value. The user requests a specific resource value, i.e. channel
|
||||
* number and it is verified unallocated, then returned.
|
||||
*
|
||||
* @param index The resource to allocate
|
||||
* @return The index of the allocated block
|
||||
* @throws CheckedAllocationException If there are no resources available to be allocated.
|
||||
*/
|
||||
public int allocate(final int index) throws CheckedAllocationException {
|
||||
if (index >= m_size || index < 0) {
|
||||
throw new CheckedAllocationException("Index " + index + " out of range");
|
||||
}
|
||||
if (m_numAllocated[index]) {
|
||||
throw new CheckedAllocationException("Resource at index " + index + " already allocated");
|
||||
}
|
||||
m_numAllocated[index] = true;
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free an allocated resource. After a resource is no longer needed, for example a destructor is
|
||||
* called for a channel assignment class, Free will release the resource value so it can be reused
|
||||
* somewhere else in the program.
|
||||
*
|
||||
* @param index The index of the resource to free.
|
||||
*/
|
||||
public void free(final int index) {
|
||||
if (!m_numAllocated[index]) {
|
||||
throw new AllocationException("No resource available to be freed");
|
||||
}
|
||||
m_numAllocated[index] = false;
|
||||
}
|
||||
|
||||
}
|
||||
273
wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java
Normal file
273
wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java
Normal file
@@ -0,0 +1,273 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.Manifest;
|
||||
//import org.opencv.core.Core;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tInstances;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.internal.HardwareHLUsageReporting;
|
||||
import edu.wpi.first.wpilibj.internal.HardwareTimer;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import edu.wpi.first.wpilibj.util.WPILibVersion;
|
||||
|
||||
/**
|
||||
* Implement a Robot Program framework. The RobotBase class is intended to be subclassed by a user
|
||||
* creating a robot program. Overridden autonomous() and operatorControl() methods are called at the
|
||||
* appropriate time as the match proceeds. In the current implementation, the Autonomous code will
|
||||
* run to completion before the OperatorControl code could start. In the future the Autonomous code
|
||||
* might be spawned as a task, then killed at the end of the Autonomous period.
|
||||
*/
|
||||
public abstract class RobotBase {
|
||||
/**
|
||||
* The VxWorks priority that robot code should work at (so Java code should run at).
|
||||
*/
|
||||
public static final int ROBOT_TASK_PRIORITY = 101;
|
||||
|
||||
/**
|
||||
* The ID of the main Java thread.
|
||||
*/
|
||||
// This is usually 1, but it is best to make sure
|
||||
public static final long MAIN_THREAD_ID = Thread.currentThread().getId();
|
||||
|
||||
protected final DriverStation m_ds;
|
||||
|
||||
/**
|
||||
* Constructor for a generic robot program. User code should be placed in the constructor that
|
||||
* runs before the Autonomous or Operator Control period starts. The constructor will run to
|
||||
* completion before Autonomous is entered.
|
||||
*
|
||||
* <p>This must be used to ensure that the communications code starts. In the future it would be
|
||||
* nice
|
||||
* to put this code into it's own task that loads on boot so ensure that it runs.
|
||||
*/
|
||||
protected RobotBase() {
|
||||
NetworkTable.setNetworkIdentity("Robot");
|
||||
NetworkTable.setPersistentFilename("/home/lvuser/networktables.ini");
|
||||
NetworkTable.setServerMode(); // must be before b
|
||||
m_ds = DriverStation.getInstance();
|
||||
NetworkTable.getTable(""); // forces network tables to initialize
|
||||
NetworkTable.getTable("LiveWindow").getSubTable("~STATUS~").putBoolean("LW Enabled", false);
|
||||
|
||||
LiveWindow.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the resources for a RobotBase class.
|
||||
*/
|
||||
public void free() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the robot is a simulation.
|
||||
*
|
||||
* @return If the robot is running in simulation.
|
||||
*/
|
||||
public static boolean isSimulation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the robot is real.
|
||||
*
|
||||
* @return If the robot is running in the real world.
|
||||
*/
|
||||
public static boolean isReal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the Robot is currently disabled.
|
||||
*
|
||||
* @return True if the Robot is currently disabled by the field controls.
|
||||
*/
|
||||
public boolean isDisabled() {
|
||||
return m_ds.isDisabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the Robot is currently enabled.
|
||||
*
|
||||
* @return True if the Robot is currently enabled by the field controls.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return m_ds.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the robot is currently in Autonomous mode as determined by the field
|
||||
* controls.
|
||||
*
|
||||
* @return True if the robot is currently operating Autonomously.
|
||||
*/
|
||||
public boolean isAutonomous() {
|
||||
return m_ds.isAutonomous();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the robot is currently in Test mode as determined by the driver
|
||||
* station.
|
||||
*
|
||||
* @return True if the robot is currently operating in Test mode.
|
||||
*/
|
||||
public boolean isTest() {
|
||||
return m_ds.isTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the robot is currently in Operator Control mode as determined by the field
|
||||
* controls.
|
||||
*
|
||||
* @return True if the robot is currently operating in Tele-Op mode.
|
||||
*/
|
||||
public boolean isOperatorControl() {
|
||||
return m_ds.isOperatorControl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if new data is available from the driver station.
|
||||
*
|
||||
* @return Has new data arrived over the network since the last time this function was called?
|
||||
*/
|
||||
public boolean isNewDataAvailable() {
|
||||
return m_ds.isNewControlData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an alternate "main loop" via startCompetition().
|
||||
*/
|
||||
public abstract void startCompetition();
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
public static boolean getBooleanProperty(String name, boolean defaultValue) {
|
||||
String propVal = System.getProperty(name);
|
||||
if (propVal == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (propVal.equalsIgnoreCase("false")) {
|
||||
return false;
|
||||
} else if (propVal.equalsIgnoreCase("true")) {
|
||||
return true;
|
||||
} else {
|
||||
throw new IllegalStateException(propVal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common initialization for all robot programs.
|
||||
*/
|
||||
public static void initializeHardwareConfiguration() {
|
||||
if (!HAL.initialize(500, 0)) {
|
||||
throw new IllegalStateException("Failed to initialize. Terminating");
|
||||
}
|
||||
|
||||
// Set some implementations so that the static methods work properly
|
||||
Timer.SetImplementation(new HardwareTimer());
|
||||
HLUsageReporting.SetImplementation(new HardwareHLUsageReporting());
|
||||
RobotState.SetImplementation(DriverStation.getInstance());
|
||||
|
||||
// Load opencv
|
||||
/* TODO (after opencv is added again)
|
||||
try {
|
||||
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||
} catch (UnsatisfiedLinkError ex) {
|
||||
System.out.println("OpenCV Native Libraries could not be loaded.");
|
||||
System.out.println("Please try redeploying, or reimage your roboRIO and try again.");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting point for the applications.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public static void main(String... args) {
|
||||
initializeHardwareConfiguration();
|
||||
|
||||
HAL.report(tResourceType.kResourceType_Language, tInstances.kLanguage_Java);
|
||||
|
||||
String robotName = "";
|
||||
Enumeration<URL> resources = null;
|
||||
try {
|
||||
resources = RobotBase.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
while (resources != null && resources.hasMoreElements()) {
|
||||
try {
|
||||
Manifest manifest = new Manifest(resources.nextElement().openStream());
|
||||
robotName = manifest.getMainAttributes().getValue("Robot-Class");
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("********** Robot program starting **********");
|
||||
|
||||
RobotBase robot;
|
||||
try {
|
||||
robot = (RobotBase) Class.forName(robotName).newInstance();
|
||||
} catch (Throwable throwable) {
|
||||
DriverStation.reportError("ERROR Unhandled exception instantiating robot " + robotName + " "
|
||||
+ throwable.toString() + " at " + Arrays.toString(throwable.getStackTrace()), false);
|
||||
System.err.println("WARNING: Robots don't quit!");
|
||||
System.err.println("ERROR: Could not instantiate robot " + robotName + "!");
|
||||
System.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final File file = new File("/tmp/frc_versions/FRC_Lib_Version.ini");
|
||||
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
file.createNewFile();
|
||||
|
||||
try (FileOutputStream output = new FileOutputStream(file)) {
|
||||
output.write("Java ".getBytes());
|
||||
output.write(WPILibVersion.Version.getBytes());
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
boolean errorOnExit = false;
|
||||
try {
|
||||
robot.startCompetition();
|
||||
} catch (Throwable throwable) {
|
||||
DriverStation.reportError(
|
||||
"ERROR Unhandled exception: " + throwable.toString() + " at "
|
||||
+ Arrays.toString(throwable.getStackTrace()), false);
|
||||
errorOnExit = true;
|
||||
} finally {
|
||||
// startCompetition never returns unless exception occurs....
|
||||
System.err.println("WARNING: Robots don't quit!");
|
||||
if (errorOnExit) {
|
||||
System.err
|
||||
.println("---> The startCompetition() method (or methods called by it) should have "
|
||||
+ "handled the exception above.");
|
||||
} else {
|
||||
System.err.println("---> Unexpected return from startCompetition() method.");
|
||||
}
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
748
wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotDrive.java
Normal file
748
wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotDrive.java
Normal file
@@ -0,0 +1,748 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tInstances;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Utility class for handling Robot drive based on a definition of the motor configuration. The
|
||||
* robot drive class handles basic driving for a robot. Currently, 2 and 4 motor tank and mecanum
|
||||
* drive trains are supported. In the future other drive types like swerve might be implemented.
|
||||
* Motor channel numbers are supplied on creation of the class. Those are used for either the drive
|
||||
* function (intended for hand created drive code, such as autonomous) or with the Tank/Arcade
|
||||
* functions intended to be used for Operator Control driving.
|
||||
*/
|
||||
public class RobotDrive implements MotorSafety {
|
||||
|
||||
protected MotorSafetyHelper m_safetyHelper;
|
||||
|
||||
/**
|
||||
* The location of a motor on the robot for the purpose of driving.
|
||||
*/
|
||||
public enum MotorType {
|
||||
kFrontLeft(0), kFrontRight(1), kRearLeft(2), kRearRight(3);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private MotorType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static final double kDefaultExpirationTime = 0.1;
|
||||
public static final double kDefaultSensitivity = 0.5;
|
||||
public static final double kDefaultMaxOutput = 1.0;
|
||||
protected static final int kMaxNumberOfMotors = 4;
|
||||
protected double m_sensitivity;
|
||||
protected double m_maxOutput;
|
||||
protected SpeedController m_frontLeftMotor;
|
||||
protected SpeedController m_frontRightMotor;
|
||||
protected SpeedController m_rearLeftMotor;
|
||||
protected SpeedController m_rearRightMotor;
|
||||
protected boolean m_allocatedSpeedControllers;
|
||||
protected static boolean kArcadeRatioCurve_Reported = false;
|
||||
protected static boolean kTank_Reported = false;
|
||||
protected static boolean kArcadeStandard_Reported = false;
|
||||
protected static boolean kMecanumCartesian_Reported = false;
|
||||
protected static boolean kMecanumPolar_Reported = false;
|
||||
|
||||
/**
|
||||
* Constructor for RobotDrive with 2 motors specified with channel numbers. Set up parameters for
|
||||
* a two wheel drive system where the left and right motor pwm channels are specified in the call.
|
||||
* This call assumes Talons for controlling the motors.
|
||||
*
|
||||
* @param leftMotorChannel The PWM channel number that drives the left motor.
|
||||
* @param rightMotorChannel The PWM channel number that drives the right motor.
|
||||
*/
|
||||
public RobotDrive(final int leftMotorChannel, final int rightMotorChannel) {
|
||||
m_sensitivity = kDefaultSensitivity;
|
||||
m_maxOutput = kDefaultMaxOutput;
|
||||
m_frontLeftMotor = null;
|
||||
m_rearLeftMotor = new Talon(leftMotorChannel);
|
||||
m_frontRightMotor = null;
|
||||
m_rearRightMotor = new Talon(rightMotorChannel);
|
||||
m_allocatedSpeedControllers = true;
|
||||
setupMotorSafety();
|
||||
drive(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for RobotDrive with 4 motors specified with channel numbers. Set up parameters for
|
||||
* a four wheel drive system where all four motor pwm channels are specified in the call. This
|
||||
* call assumes Talons for controlling the motors.
|
||||
*
|
||||
* @param frontLeftMotor Front left motor channel number
|
||||
* @param rearLeftMotor Rear Left motor channel number
|
||||
* @param frontRightMotor Front right motor channel number
|
||||
* @param rearRightMotor Rear Right motor channel number
|
||||
*/
|
||||
public RobotDrive(final int frontLeftMotor, final int rearLeftMotor, final int frontRightMotor,
|
||||
final int rearRightMotor) {
|
||||
m_sensitivity = kDefaultSensitivity;
|
||||
m_maxOutput = kDefaultMaxOutput;
|
||||
m_rearLeftMotor = new Talon(rearLeftMotor);
|
||||
m_rearRightMotor = new Talon(rearRightMotor);
|
||||
m_frontLeftMotor = new Talon(frontLeftMotor);
|
||||
m_frontRightMotor = new Talon(frontRightMotor);
|
||||
m_allocatedSpeedControllers = true;
|
||||
setupMotorSafety();
|
||||
drive(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for RobotDrive with 2 motors specified as SpeedController objects. The
|
||||
* SpeedController version of the constructor enables programs to use the RobotDrive classes with
|
||||
* subclasses of the SpeedController objects, for example, versions with ramping or reshaping of
|
||||
* the curve to suit motor bias or dead-band elimination.
|
||||
*
|
||||
* @param leftMotor The left SpeedController object used to drive the robot.
|
||||
* @param rightMotor the right SpeedController object used to drive the robot.
|
||||
*/
|
||||
public RobotDrive(SpeedController leftMotor, SpeedController rightMotor) {
|
||||
requireNonNull(leftMotor, "Provided left motor was null");
|
||||
requireNonNull(rightMotor, "Provided right motor was null");
|
||||
|
||||
m_frontLeftMotor = null;
|
||||
m_rearLeftMotor = leftMotor;
|
||||
m_frontRightMotor = null;
|
||||
m_rearRightMotor = rightMotor;
|
||||
m_sensitivity = kDefaultSensitivity;
|
||||
m_maxOutput = kDefaultMaxOutput;
|
||||
m_allocatedSpeedControllers = false;
|
||||
setupMotorSafety();
|
||||
drive(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for RobotDrive with 4 motors specified as SpeedController objects. Speed controller
|
||||
* input version of RobotDrive (see previous comments).
|
||||
*
|
||||
* @param frontLeftMotor The front left SpeedController object used to drive the robot
|
||||
* @param rearLeftMotor The back left SpeedController object used to drive the robot.
|
||||
* @param frontRightMotor The front right SpeedController object used to drive the robot.
|
||||
* @param rearRightMotor The back right SpeedController object used to drive the robot.
|
||||
*/
|
||||
public RobotDrive(SpeedController frontLeftMotor, SpeedController rearLeftMotor,
|
||||
SpeedController frontRightMotor, SpeedController rearRightMotor) {
|
||||
m_frontLeftMotor = requireNonNull(frontLeftMotor, "frontLeftMotor cannot be null");
|
||||
m_rearLeftMotor = requireNonNull(rearLeftMotor, "rearLeftMotor cannot be null");
|
||||
m_frontRightMotor = requireNonNull(frontRightMotor, "frontRightMotor cannot be null");
|
||||
m_rearRightMotor = requireNonNull(rearRightMotor, "rearRightMotor cannot be null");
|
||||
m_sensitivity = kDefaultSensitivity;
|
||||
m_maxOutput = kDefaultMaxOutput;
|
||||
m_allocatedSpeedControllers = false;
|
||||
setupMotorSafety();
|
||||
drive(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive the motors at "outputMagnitude" and "curve". Both outputMagnitude and curve are -1.0 to
|
||||
* +1.0 values, where 0.0 represents stopped and not turning. {@literal curve < 0 will turn left
|
||||
* and curve > 0} will turn right.
|
||||
*
|
||||
* <p>The algorithm for steering provides a constant turn radius for any normal speed range, both
|
||||
* forward and backward. Increasing sensitivity causes sharper turns for fixed values of curve.
|
||||
*
|
||||
* <p>This function will most likely be used in an autonomous routine.
|
||||
*
|
||||
* @param outputMagnitude The speed setting for the outside wheel in a turn, forward or backwards,
|
||||
* +1 to -1.
|
||||
* @param curve The rate of turn, constant for different forward speeds. Set {@literal
|
||||
* curve < 0 for left turn or curve > 0 for right turn.} Set curve =
|
||||
* e^(-r/w) to get a turn radius r for wheelbase w of your robot.
|
||||
* Conversely, turn radius r = -ln(curve)*w for a given value of curve and
|
||||
* wheelbase w.
|
||||
*/
|
||||
public void drive(double outputMagnitude, double curve) {
|
||||
final double leftOutput;
|
||||
final double rightOutput;
|
||||
|
||||
if (!kArcadeRatioCurve_Reported) {
|
||||
HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
|
||||
tInstances.kRobotDrive_ArcadeRatioCurve);
|
||||
kArcadeRatioCurve_Reported = true;
|
||||
}
|
||||
if (curve < 0) {
|
||||
double value = Math.log(-curve);
|
||||
double ratio = (value - m_sensitivity) / (value + m_sensitivity);
|
||||
if (ratio == 0) {
|
||||
ratio = .0000000001;
|
||||
}
|
||||
leftOutput = outputMagnitude / ratio;
|
||||
rightOutput = outputMagnitude;
|
||||
} else if (curve > 0) {
|
||||
double value = Math.log(curve);
|
||||
double ratio = (value - m_sensitivity) / (value + m_sensitivity);
|
||||
if (ratio == 0) {
|
||||
ratio = .0000000001;
|
||||
}
|
||||
leftOutput = outputMagnitude;
|
||||
rightOutput = outputMagnitude / ratio;
|
||||
} else {
|
||||
leftOutput = outputMagnitude;
|
||||
rightOutput = outputMagnitude;
|
||||
}
|
||||
setLeftRightMotorOutputs(leftOutput, rightOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide tank steering using the stored robot configuration. drive the robot using two joystick
|
||||
* inputs. The Y-axis will be selected from each Joystick object.
|
||||
*
|
||||
* @param leftStick The joystick to control the left side of the robot.
|
||||
* @param rightStick The joystick to control the right side of the robot.
|
||||
*/
|
||||
public void tankDrive(GenericHID leftStick, GenericHID rightStick) {
|
||||
requireNonNull(leftStick, "Provided left stick was null");
|
||||
requireNonNull(rightStick, "Provided right stick was null");
|
||||
|
||||
tankDrive(leftStick.getY(), rightStick.getY(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide tank steering using the stored robot configuration. drive the robot using two joystick
|
||||
* inputs. The Y-axis will be selected from each Joystick object.
|
||||
*
|
||||
* @param leftStick The joystick to control the left side of the robot.
|
||||
* @param rightStick The joystick to control the right side of the robot.
|
||||
* @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds
|
||||
*/
|
||||
public void tankDrive(GenericHID leftStick, GenericHID rightStick, boolean squaredInputs) {
|
||||
requireNonNull(leftStick, "Provided left stick was null");
|
||||
requireNonNull(rightStick, "Provided right stick was null");
|
||||
|
||||
tankDrive(leftStick.getY(), rightStick.getY(), squaredInputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide tank steering using the stored robot configuration. This function lets you pick the
|
||||
* axis to be used on each Joystick object for the left and right sides of the robot.
|
||||
*
|
||||
* @param leftStick The Joystick object to use for the left side of the robot.
|
||||
* @param leftAxis The axis to select on the left side Joystick object.
|
||||
* @param rightStick The Joystick object to use for the right side of the robot.
|
||||
* @param rightAxis The axis to select on the right side Joystick object.
|
||||
*/
|
||||
public void tankDrive(GenericHID leftStick, final int leftAxis, GenericHID rightStick,
|
||||
final int rightAxis) {
|
||||
requireNonNull(leftStick, "Provided left stick was null");
|
||||
requireNonNull(rightStick, "Provided right stick was null");
|
||||
|
||||
tankDrive(leftStick.getRawAxis(leftAxis), rightStick.getRawAxis(rightAxis), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide tank steering using the stored robot configuration. This function lets you pick the
|
||||
* axis to be used on each Joystick object for the left and right sides of the robot.
|
||||
*
|
||||
* @param leftStick The Joystick object to use for the left side of the robot.
|
||||
* @param leftAxis The axis to select on the left side Joystick object.
|
||||
* @param rightStick The Joystick object to use for the right side of the robot.
|
||||
* @param rightAxis The axis to select on the right side Joystick object.
|
||||
* @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds
|
||||
*/
|
||||
public void tankDrive(GenericHID leftStick, final int leftAxis, GenericHID rightStick,
|
||||
final int rightAxis, boolean squaredInputs) {
|
||||
requireNonNull(leftStick, "Provided left stick was null");
|
||||
requireNonNull(rightStick, "Provided right stick was null");
|
||||
|
||||
tankDrive(leftStick.getRawAxis(leftAxis), rightStick.getRawAxis(rightAxis), squaredInputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide tank steering using the stored robot configuration. This function lets you directly
|
||||
* provide joystick values from any source.
|
||||
*
|
||||
* @param leftValue The value of the left stick.
|
||||
* @param rightValue The value of the right stick.
|
||||
* @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds
|
||||
*/
|
||||
public void tankDrive(double leftValue, double rightValue, boolean squaredInputs) {
|
||||
|
||||
if (!kTank_Reported) {
|
||||
HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
|
||||
tInstances.kRobotDrive_Tank);
|
||||
kTank_Reported = true;
|
||||
}
|
||||
|
||||
leftValue = limit(leftValue);
|
||||
rightValue = limit(rightValue);
|
||||
|
||||
// square the inputs (while preserving the sign) to increase fine control
|
||||
// while permitting full power
|
||||
if (squaredInputs) {
|
||||
leftValue = Math.copySign(leftValue * leftValue, leftValue);
|
||||
rightValue = Math.copySign(rightValue * rightValue, rightValue);
|
||||
}
|
||||
setLeftRightMotorOutputs(leftValue, rightValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide tank steering using the stored robot configuration. This function lets you directly
|
||||
* provide joystick values from any source.
|
||||
*
|
||||
* @param leftValue The value of the left stick.
|
||||
* @param rightValue The value of the right stick.
|
||||
*/
|
||||
public void tankDrive(double leftValue, double rightValue) {
|
||||
tankDrive(leftValue, rightValue, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Arcade drive implements single stick driving. Given a single Joystick, the class assumes the Y
|
||||
* axis for the move value and the X axis for the rotate value. (Should add more information here
|
||||
* regarding the way that arcade drive works.)
|
||||
*
|
||||
* @param stick The joystick to use for Arcade single-stick driving. The Y-axis will be
|
||||
* selected for forwards/backwards and the X-axis will be selected for
|
||||
* rotation rate.
|
||||
* @param squaredInputs If true, the sensitivity will be decreased for small values
|
||||
*/
|
||||
public void arcadeDrive(GenericHID stick, boolean squaredInputs) {
|
||||
// simply call the full-featured arcadeDrive with the appropriate values
|
||||
arcadeDrive(stick.getY(), stick.getX(), squaredInputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Arcade drive implements single stick driving. Given a single Joystick, the class assumes the Y
|
||||
* axis for the move value and the X axis for the rotate value. (Should add more information here
|
||||
* regarding the way that arcade drive works.)
|
||||
*
|
||||
* @param stick The joystick to use for Arcade single-stick driving. The Y-axis will be selected
|
||||
* for forwards/backwards and the X-axis will be selected for rotation rate.
|
||||
*/
|
||||
public void arcadeDrive(GenericHID stick) {
|
||||
arcadeDrive(stick, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Arcade drive implements single stick driving. Given two joystick instances and two axis,
|
||||
* compute the values to send to either two or four motors.
|
||||
*
|
||||
* @param moveStick The Joystick object that represents the forward/backward direction
|
||||
* @param moveAxis The axis on the moveStick object to use for forwards/backwards (typically
|
||||
* Y_AXIS)
|
||||
* @param rotateStick The Joystick object that represents the rotation value
|
||||
* @param rotateAxis The axis on the rotation object to use for the rotate right/left
|
||||
* (typically X_AXIS)
|
||||
* @param squaredInputs Setting this parameter to true decreases the sensitivity at lower speeds
|
||||
*/
|
||||
public void arcadeDrive(GenericHID moveStick, final int moveAxis, GenericHID rotateStick,
|
||||
final int rotateAxis, boolean squaredInputs) {
|
||||
double moveValue = moveStick.getRawAxis(moveAxis);
|
||||
double rotateValue = rotateStick.getRawAxis(rotateAxis);
|
||||
|
||||
arcadeDrive(moveValue, rotateValue, squaredInputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Arcade drive implements single stick driving. Given two joystick instances and two axis,
|
||||
* compute the values to send to either two or four motors.
|
||||
*
|
||||
* @param moveStick The Joystick object that represents the forward/backward direction
|
||||
* @param moveAxis The axis on the moveStick object to use for forwards/backwards (typically
|
||||
* Y_AXIS)
|
||||
* @param rotateStick The Joystick object that represents the rotation value
|
||||
* @param rotateAxis The axis on the rotation object to use for the rotate right/left (typically
|
||||
* X_AXIS)
|
||||
*/
|
||||
public void arcadeDrive(GenericHID moveStick, final int moveAxis, GenericHID rotateStick,
|
||||
final int rotateAxis) {
|
||||
arcadeDrive(moveStick, moveAxis, rotateStick, rotateAxis, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Arcade drive implements single stick driving. This function lets you directly provide
|
||||
* joystick values from any source.
|
||||
*
|
||||
* @param moveValue The value to use for forwards/backwards
|
||||
* @param rotateValue The value to use for the rotate right/left
|
||||
* @param squaredInputs If set, decreases the sensitivity at low speeds
|
||||
*/
|
||||
public void arcadeDrive(double moveValue, double rotateValue, boolean squaredInputs) {
|
||||
// local variables to hold the computed PWM values for the motors
|
||||
if (!kArcadeStandard_Reported) {
|
||||
HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
|
||||
tInstances.kRobotDrive_ArcadeStandard);
|
||||
kArcadeStandard_Reported = true;
|
||||
}
|
||||
|
||||
double leftMotorSpeed;
|
||||
double rightMotorSpeed;
|
||||
|
||||
moveValue = limit(moveValue);
|
||||
rotateValue = limit(rotateValue);
|
||||
|
||||
// square the inputs (while preserving the sign) to increase fine control
|
||||
// while permitting full power
|
||||
if (squaredInputs) {
|
||||
// square the inputs (while preserving the sign) to increase fine control
|
||||
// while permitting full power
|
||||
moveValue = Math.copySign(moveValue * moveValue, moveValue);
|
||||
rotateValue = Math.copySign(rotateValue * rotateValue, rotateValue);
|
||||
}
|
||||
|
||||
if (moveValue > 0.0) {
|
||||
if (rotateValue > 0.0) {
|
||||
leftMotorSpeed = moveValue - rotateValue;
|
||||
rightMotorSpeed = Math.max(moveValue, rotateValue);
|
||||
} else {
|
||||
leftMotorSpeed = Math.max(moveValue, -rotateValue);
|
||||
rightMotorSpeed = moveValue + rotateValue;
|
||||
}
|
||||
} else {
|
||||
if (rotateValue > 0.0) {
|
||||
leftMotorSpeed = -Math.max(-moveValue, rotateValue);
|
||||
rightMotorSpeed = moveValue + rotateValue;
|
||||
} else {
|
||||
leftMotorSpeed = moveValue - rotateValue;
|
||||
rightMotorSpeed = -Math.max(-moveValue, -rotateValue);
|
||||
}
|
||||
}
|
||||
|
||||
setLeftRightMotorOutputs(leftMotorSpeed, rightMotorSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Arcade drive implements single stick driving. This function lets you directly provide
|
||||
* joystick values from any source.
|
||||
*
|
||||
* @param moveValue The value to use for fowards/backwards
|
||||
* @param rotateValue The value to use for the rotate right/left
|
||||
*/
|
||||
public void arcadeDrive(double moveValue, double rotateValue) {
|
||||
arcadeDrive(moveValue, rotateValue, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive method for Mecanum wheeled robots.
|
||||
*
|
||||
* <p>A method for driving with Mecanum wheeled robots. There are 4 wheels on the robot, arranged
|
||||
* so that the front and back wheels are toed in 45 degrees. When looking at the wheels from the
|
||||
* top, the roller axles should form an X across the robot.
|
||||
*
|
||||
* <p>This is designed to be directly driven by joystick axes.
|
||||
*
|
||||
* @param x The speed that the robot should drive in the X direction. [-1.0..1.0]
|
||||
* @param y The speed that the robot should drive in the Y direction. This input is
|
||||
* inverted to match the forward == -1.0 that joysticks produce. [-1.0..1.0]
|
||||
* @param rotation The rate of rotation for the robot that is completely independent of the
|
||||
* translation. [-1.0..1.0]
|
||||
* @param gyroAngle The current angle reading from the gyro. Use this to implement field-oriented
|
||||
* controls.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public void mecanumDrive_Cartesian(double x, double y, double rotation, double gyroAngle) {
|
||||
if (!kMecanumCartesian_Reported) {
|
||||
HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
|
||||
tInstances.kRobotDrive_MecanumCartesian);
|
||||
kMecanumCartesian_Reported = true;
|
||||
}
|
||||
@SuppressWarnings("LocalVariableName")
|
||||
double xIn = x;
|
||||
@SuppressWarnings("LocalVariableName")
|
||||
double yIn = y;
|
||||
// Negate y for the joystick.
|
||||
yIn = -yIn;
|
||||
// Compenstate for gyro angle.
|
||||
double[] rotated = rotateVector(xIn, yIn, gyroAngle);
|
||||
xIn = rotated[0];
|
||||
yIn = rotated[1];
|
||||
|
||||
double[] wheelSpeeds = new double[kMaxNumberOfMotors];
|
||||
wheelSpeeds[MotorType.kFrontLeft.value] = xIn + yIn + rotation;
|
||||
wheelSpeeds[MotorType.kFrontRight.value] = -xIn + yIn - rotation;
|
||||
wheelSpeeds[MotorType.kRearLeft.value] = -xIn + yIn + rotation;
|
||||
wheelSpeeds[MotorType.kRearRight.value] = xIn + yIn - rotation;
|
||||
|
||||
normalize(wheelSpeeds);
|
||||
m_frontLeftMotor.set(wheelSpeeds[MotorType.kFrontLeft.value] * m_maxOutput);
|
||||
m_frontRightMotor.set(wheelSpeeds[MotorType.kFrontRight.value] * m_maxOutput);
|
||||
m_rearLeftMotor.set(wheelSpeeds[MotorType.kRearLeft.value] * m_maxOutput);
|
||||
m_rearRightMotor.set(wheelSpeeds[MotorType.kRearRight.value] * m_maxOutput);
|
||||
|
||||
if (m_safetyHelper != null) {
|
||||
m_safetyHelper.feed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drive method for Mecanum wheeled robots.
|
||||
*
|
||||
* <p>A method for driving with Mecanum wheeled robots. There are 4 wheels on the robot, arranged
|
||||
* so that the front and back wheels are toed in 45 degrees. When looking at the wheels from the
|
||||
* top, the roller axles should form an X across the robot.
|
||||
*
|
||||
* @param magnitude The speed that the robot should drive in a given direction.
|
||||
* @param direction The direction the robot should drive in degrees. The direction and maginitute
|
||||
* are independent of the rotation rate.
|
||||
* @param rotation The rate of rotation for the robot that is completely independent of the
|
||||
* magnitute or direction. [-1.0..1.0]
|
||||
*/
|
||||
public void mecanumDrive_Polar(double magnitude, double direction, double rotation) {
|
||||
if (!kMecanumPolar_Reported) {
|
||||
HAL.report(tResourceType.kResourceType_RobotDrive, getNumMotors(),
|
||||
tInstances.kRobotDrive_MecanumPolar);
|
||||
kMecanumPolar_Reported = true;
|
||||
}
|
||||
// Normalized for full power along the Cartesian axes.
|
||||
magnitude = limit(magnitude) * Math.sqrt(2.0);
|
||||
// The rollers are at 45 degree angles.
|
||||
double dirInRad = (direction + 45.0) * 3.14159 / 180.0;
|
||||
double cosD = Math.cos(dirInRad);
|
||||
double sinD = Math.sin(dirInRad);
|
||||
|
||||
double[] wheelSpeeds = new double[kMaxNumberOfMotors];
|
||||
wheelSpeeds[MotorType.kFrontLeft.value] = sinD * magnitude + rotation;
|
||||
wheelSpeeds[MotorType.kFrontRight.value] = cosD * magnitude - rotation;
|
||||
wheelSpeeds[MotorType.kRearLeft.value] = cosD * magnitude + rotation;
|
||||
wheelSpeeds[MotorType.kRearRight.value] = sinD * magnitude - rotation;
|
||||
|
||||
normalize(wheelSpeeds);
|
||||
|
||||
m_frontLeftMotor.set(wheelSpeeds[MotorType.kFrontLeft.value] * m_maxOutput);
|
||||
m_frontRightMotor.set(wheelSpeeds[MotorType.kFrontRight.value] * m_maxOutput);
|
||||
m_rearLeftMotor.set(wheelSpeeds[MotorType.kRearLeft.value] * m_maxOutput);
|
||||
m_rearRightMotor.set(wheelSpeeds[MotorType.kRearRight.value] * m_maxOutput);
|
||||
|
||||
if (m_safetyHelper != null) {
|
||||
m_safetyHelper.feed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holonomic Drive method for Mecanum wheeled robots.
|
||||
*
|
||||
* <p>This is an alias to mecanumDrive_Polar() for backward compatability
|
||||
*
|
||||
* @param magnitude The speed that the robot should drive in a given direction. [-1.0..1.0]
|
||||
* @param direction The direction the robot should drive. The direction and maginitute are
|
||||
* independent of the rotation rate.
|
||||
* @param rotation The rate of rotation for the robot that is completely independent of the
|
||||
* magnitute or direction. [-1.0..1.0]
|
||||
*/
|
||||
void holonomicDrive(double magnitude, double direction, double rotation) {
|
||||
mecanumDrive_Polar(magnitude, direction, rotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the speed of the right and left motors. This is used once an appropriate drive setup
|
||||
* function is called such as twoWheelDrive(). The motors are set to "leftSpeed" and
|
||||
* "rightSpeed" and includes flipping the direction of one side for opposing motors.
|
||||
*
|
||||
* @param leftOutput The speed to send to the left side of the robot.
|
||||
* @param rightOutput The speed to send to the right side of the robot.
|
||||
*/
|
||||
public void setLeftRightMotorOutputs(double leftOutput, double rightOutput) {
|
||||
requireNonNull(m_rearLeftMotor, "Provided left motor was null");
|
||||
requireNonNull(m_rearRightMotor, "Provided right motor was null");
|
||||
|
||||
if (m_frontLeftMotor != null) {
|
||||
m_frontLeftMotor.set(limit(leftOutput) * m_maxOutput);
|
||||
}
|
||||
m_rearLeftMotor.set(limit(leftOutput) * m_maxOutput);
|
||||
|
||||
if (m_frontRightMotor != null) {
|
||||
m_frontRightMotor.set(-limit(rightOutput) * m_maxOutput);
|
||||
}
|
||||
m_rearRightMotor.set(-limit(rightOutput) * m_maxOutput);
|
||||
|
||||
if (m_safetyHelper != null) {
|
||||
m_safetyHelper.feed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit motor values to the -1.0 to +1.0 range.
|
||||
*/
|
||||
protected static double limit(double number) {
|
||||
if (number > 1.0) {
|
||||
return 1.0;
|
||||
}
|
||||
if (number < -1.0) {
|
||||
return -1.0;
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize all wheel speeds if the magnitude of any wheel is greater than 1.0.
|
||||
*/
|
||||
protected static void normalize(double[] wheelSpeeds) {
|
||||
double maxMagnitude = Math.abs(wheelSpeeds[0]);
|
||||
for (int i = 1; i < kMaxNumberOfMotors; i++) {
|
||||
double temp = Math.abs(wheelSpeeds[i]);
|
||||
if (maxMagnitude < temp) {
|
||||
maxMagnitude = temp;
|
||||
}
|
||||
}
|
||||
if (maxMagnitude > 1.0) {
|
||||
for (int i = 0; i < kMaxNumberOfMotors; i++) {
|
||||
wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate a vector in Cartesian space.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
protected static double[] rotateVector(double x, double y, double angle) {
|
||||
double cosA = Math.cos(angle * (3.14159 / 180.0));
|
||||
double sinA = Math.sin(angle * (3.14159 / 180.0));
|
||||
double[] out = new double[2];
|
||||
out[0] = x * cosA - y * sinA;
|
||||
out[1] = x * sinA + y * cosA;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invert a motor direction. This is used when a motor should run in the opposite direction as the
|
||||
* drive code would normally run it. Motors that are direct drive would be inverted, the drive
|
||||
* code assumes that the motors are geared with one reversal.
|
||||
*
|
||||
* @param motor The motor index to invert.
|
||||
* @param isInverted True if the motor should be inverted when operated.
|
||||
*/
|
||||
public void setInvertedMotor(MotorType motor, boolean isInverted) {
|
||||
switch (motor) {
|
||||
case kFrontLeft:
|
||||
m_frontLeftMotor.setInverted(isInverted);
|
||||
break;
|
||||
case kFrontRight:
|
||||
m_frontRightMotor.setInverted(isInverted);
|
||||
break;
|
||||
case kRearLeft:
|
||||
m_rearLeftMotor.setInverted(isInverted);
|
||||
break;
|
||||
case kRearRight:
|
||||
m_rearRightMotor.setInverted(isInverted);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal motor type: " + motor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the turning sensitivity.
|
||||
*
|
||||
* <p>This only impacts the drive() entry-point.
|
||||
*
|
||||
* @param sensitivity Effectively sets the turning sensitivity (or turn radius for a given value)
|
||||
*/
|
||||
public void setSensitivity(double sensitivity) {
|
||||
m_sensitivity = sensitivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the scaling factor for using RobotDrive with motor controllers in a mode other than
|
||||
* PercentVbus.
|
||||
*
|
||||
* @param maxOutput Multiplied with the output percentage computed by the drive functions.
|
||||
*/
|
||||
public void setMaxOutput(double maxOutput) {
|
||||
m_maxOutput = maxOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the speed controllers if they were allocated locally.
|
||||
*/
|
||||
public void free() {
|
||||
if (m_allocatedSpeedControllers) {
|
||||
if (m_frontLeftMotor != null) {
|
||||
((PWM) m_frontLeftMotor).free();
|
||||
}
|
||||
if (m_frontRightMotor != null) {
|
||||
((PWM) m_frontRightMotor).free();
|
||||
}
|
||||
if (m_rearLeftMotor != null) {
|
||||
((PWM) m_rearLeftMotor).free();
|
||||
}
|
||||
if (m_rearRightMotor != null) {
|
||||
((PWM) m_rearRightMotor).free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpiration(double timeout) {
|
||||
m_safetyHelper.setExpiration(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getExpiration() {
|
||||
return m_safetyHelper.getExpiration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return m_safetyHelper.isAlive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSafetyEnabled() {
|
||||
return m_safetyHelper.isSafetyEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSafetyEnabled(boolean enabled) {
|
||||
m_safetyHelper.setSafetyEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Robot Drive";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopMotor() {
|
||||
if (m_frontLeftMotor != null) {
|
||||
m_frontLeftMotor.stopMotor();
|
||||
}
|
||||
if (m_frontRightMotor != null) {
|
||||
m_frontRightMotor.stopMotor();
|
||||
}
|
||||
if (m_rearLeftMotor != null) {
|
||||
m_rearLeftMotor.stopMotor();
|
||||
}
|
||||
if (m_rearRightMotor != null) {
|
||||
m_rearRightMotor.stopMotor();
|
||||
}
|
||||
if (m_safetyHelper != null) {
|
||||
m_safetyHelper.feed();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupMotorSafety() {
|
||||
m_safetyHelper = new MotorSafetyHelper(this);
|
||||
m_safetyHelper.setExpiration(kDefaultExpirationTime);
|
||||
m_safetyHelper.setSafetyEnabled(true);
|
||||
}
|
||||
|
||||
protected int getNumMotors() {
|
||||
int motors = 0;
|
||||
if (m_frontLeftMotor != null) {
|
||||
motors++;
|
||||
}
|
||||
if (m_frontRightMotor != null) {
|
||||
motors++;
|
||||
}
|
||||
if (m_rearLeftMotor != null) {
|
||||
motors++;
|
||||
}
|
||||
if (m_rearRightMotor != null) {
|
||||
motors++;
|
||||
}
|
||||
return motors;
|
||||
}
|
||||
}
|
||||
72
wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotState.java
Normal file
72
wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotState.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.util.BaseSystemNotInitializedException;
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
public class RobotState {
|
||||
private static Interface m_impl;
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
public static void SetImplementation(Interface implementation) {
|
||||
m_impl = implementation;
|
||||
}
|
||||
|
||||
public static boolean isDisabled() {
|
||||
if (m_impl != null) {
|
||||
return m_impl.isDisabled();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(Interface.class, RobotState.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
if (m_impl != null) {
|
||||
return m_impl.isEnabled();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(Interface.class, RobotState.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isOperatorControl() {
|
||||
if (m_impl != null) {
|
||||
return m_impl.isOperatorControl();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(Interface.class, RobotState.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAutonomous() {
|
||||
if (m_impl != null) {
|
||||
return m_impl.isAutonomous();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(Interface.class, RobotState.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isTest() {
|
||||
if (m_impl != null) {
|
||||
return m_impl.isTest();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(Interface.class, RobotState.class);
|
||||
}
|
||||
}
|
||||
|
||||
interface Interface {
|
||||
boolean isDisabled();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
boolean isOperatorControl();
|
||||
|
||||
boolean isAutonomous();
|
||||
|
||||
boolean isTest();
|
||||
}
|
||||
}
|
||||
52
wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java
Normal file
52
wpilibj/src/main/java/edu/wpi/first/wpilibj/SD540.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* Mindsensors SD540 Speed Controller.
|
||||
*/
|
||||
public class SD540 extends PWMSpeedController {
|
||||
|
||||
/**
|
||||
* Common initialization code called by all constructors.
|
||||
*
|
||||
* <p>Note that the SD540 uses the following bounds for PWM values. These values should work
|
||||
* reasonably well for most controllers, but if users experience issues such as asymmetric
|
||||
* behavior around the deadband or inability to saturate the controller in either direction,
|
||||
* calibration is recommended. The calibration procedure can be found in the SD540 User Manual
|
||||
* available from Mindsensors.
|
||||
*
|
||||
* <p>- 2.05ms = full "forward" - 1.55ms = the "high end" of the deadband range - 1.50ms = center
|
||||
* of the deadband range (off) - 1.44ms = the "low end" of the deadband range - .94ms = full
|
||||
* "reverse"
|
||||
*/
|
||||
protected void initSD540() {
|
||||
setBounds(2.05, 1.55, 1.50, 1.44, .94);
|
||||
setPeriodMultiplier(PeriodMultiplier.k1X);
|
||||
setSpeed(0.0);
|
||||
setZeroLatch();
|
||||
|
||||
LiveWindow.addActuator("SD540", getChannel(), this);
|
||||
HAL.report(tResourceType.kResourceType_MindsensorsSD540, getChannel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param channel The PWM channel that the SD540 is attached to. 0-9 are on-board, 10-19 are on
|
||||
* the MXP port
|
||||
*/
|
||||
public SD540(final int channel) {
|
||||
super(channel);
|
||||
initSD540();
|
||||
}
|
||||
}
|
||||
372
wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java
Normal file
372
wpilibj/src/main/java/edu/wpi/first/wpilibj/SPI.java
Normal file
@@ -0,0 +1,372 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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 java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.hal.SPIJNI;
|
||||
|
||||
/**
|
||||
* Represents a SPI bus port.
|
||||
*/
|
||||
public class SPI extends SensorBase {
|
||||
|
||||
public enum Port {
|
||||
kOnboardCS0(0), kOnboardCS1(1), kOnboardCS2(2), kOnboardCS3(3), kMXP(4);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public int value;
|
||||
|
||||
private Port(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static int devices = 0;
|
||||
|
||||
private int m_port;
|
||||
private int m_bitOrder;
|
||||
private int m_clockPolarity;
|
||||
private int m_dataOnTrailing;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param port the physical SPI port
|
||||
*/
|
||||
public SPI(Port port) {
|
||||
m_port = (byte) port.value;
|
||||
devices++;
|
||||
|
||||
SPIJNI.spiInitialize(m_port);
|
||||
|
||||
HAL.report(tResourceType.kResourceType_SPI, devices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the resources used by this object.
|
||||
*/
|
||||
public void free() {
|
||||
SPIJNI.spiClose(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate of the generated clock signal. The default value is 500,000 Hz. The maximum
|
||||
* value is 4,000,000 Hz.
|
||||
*
|
||||
* @param hz The clock rate in Hertz.
|
||||
*/
|
||||
public final void setClockRate(int hz) {
|
||||
SPIJNI.spiSetSpeed(m_port, hz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the order that bits are sent and received on the wire to be most significant bit
|
||||
* first.
|
||||
*/
|
||||
public final void setMSBFirst() {
|
||||
m_bitOrder = 1;
|
||||
SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the order that bits are sent and received on the wire to be least significant bit
|
||||
* first.
|
||||
*/
|
||||
public final void setLSBFirst() {
|
||||
m_bitOrder = 0;
|
||||
SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the clock output line to be active low. This is sometimes called clock polarity high
|
||||
* or clock idle high.
|
||||
*/
|
||||
public final void setClockActiveLow() {
|
||||
m_clockPolarity = 1;
|
||||
SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the clock output line to be active high. This is sometimes called clock polarity low
|
||||
* or clock idle low.
|
||||
*/
|
||||
public final void setClockActiveHigh() {
|
||||
m_clockPolarity = 0;
|
||||
SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure that the data is stable on the falling edge and the data changes on the rising edge.
|
||||
*/
|
||||
public final void setSampleDataOnFalling() {
|
||||
m_dataOnTrailing = 1;
|
||||
SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure that the data is stable on the rising edge and the data changes on the falling edge.
|
||||
*/
|
||||
public final void setSampleDataOnRising() {
|
||||
m_dataOnTrailing = 0;
|
||||
SPIJNI.spiSetOpts(m_port, m_bitOrder, m_dataOnTrailing, m_clockPolarity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the chip select line to be active high.
|
||||
*/
|
||||
public final void setChipSelectActiveHigh() {
|
||||
SPIJNI.spiSetChipSelectActiveHigh(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the chip select line to be active low.
|
||||
*/
|
||||
public final void setChipSelectActiveLow() {
|
||||
SPIJNI.spiSetChipSelectActiveLow(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the slave device. Blocks until there is space in the output FIFO.
|
||||
*
|
||||
* <p>If not running in output only mode, also saves the data received on the MISO input during
|
||||
* the transfer into the receive FIFO.
|
||||
*/
|
||||
public int write(byte[] dataToSend, int size) {
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
|
||||
dataToSendBuffer.put(dataToSend);
|
||||
return SPIJNI.spiWrite(m_port, dataToSendBuffer, (byte) size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the slave device. Blocks until there is space in the output FIFO.
|
||||
*
|
||||
* <p>If not running in output only mode, also saves the data received on the MISO input during
|
||||
* the transfer into the receive FIFO.
|
||||
*
|
||||
* @param dataToSend The buffer containing the data to send. Must be created using
|
||||
* ByteBuffer.allocateDirect().
|
||||
*/
|
||||
public int write(ByteBuffer dataToSend, int size) {
|
||||
if (!dataToSend.isDirect()) {
|
||||
throw new IllegalArgumentException("must be a direct buffer");
|
||||
}
|
||||
if (dataToSend.capacity() < size) {
|
||||
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
|
||||
}
|
||||
return SPIJNI.spiWrite(m_port, dataToSend, (byte) size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a word from the receive FIFO.
|
||||
*
|
||||
* <p>Waits for the current transfer to complete if the receive FIFO is empty.
|
||||
*
|
||||
* <p>If the receive FIFO is empty, there is no active transfer, and initiate is false, errors.
|
||||
*
|
||||
* @param initiate If true, this function pushes "0" into the transmit buffer and initiates a
|
||||
* transfer. If false, this function assumes that data is already in the receive
|
||||
* FIFO from a previous write.
|
||||
*/
|
||||
public int read(boolean initiate, byte[] dataReceived, int size) {
|
||||
final int retVal;
|
||||
ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(size);
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
|
||||
if (initiate) {
|
||||
retVal = SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceivedBuffer, (byte) size);
|
||||
} else {
|
||||
retVal = SPIJNI.spiRead(m_port, dataReceivedBuffer, (byte) size);
|
||||
}
|
||||
dataReceivedBuffer.get(dataReceived);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a word from the receive FIFO.
|
||||
*
|
||||
* <p>Waits for the current transfer to complete if the receive FIFO is empty.
|
||||
*
|
||||
* <p>If the receive FIFO is empty, there is no active transfer, and initiate is false, errors.
|
||||
*
|
||||
* @param initiate If true, this function pushes "0" into the transmit buffer and initiates
|
||||
* a transfer. If false, this function assumes that data is already in the
|
||||
* receive FIFO from a previous write.
|
||||
* @param dataReceived The buffer to be filled with the received data. Must be created using
|
||||
* ByteBuffer.allocateDirect().
|
||||
* @param size The length of the transaction, in bytes
|
||||
*/
|
||||
public int read(boolean initiate, ByteBuffer dataReceived, int size) {
|
||||
if (!dataReceived.isDirect()) {
|
||||
throw new IllegalArgumentException("must be a direct buffer");
|
||||
}
|
||||
if (dataReceived.capacity() < size) {
|
||||
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
|
||||
}
|
||||
if (initiate) {
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
|
||||
return SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceived, (byte) size);
|
||||
}
|
||||
return SPIJNI.spiRead(m_port, dataReceived, (byte) size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a simultaneous read/write transaction with the device.
|
||||
*
|
||||
* @param dataToSend The data to be written out to the device
|
||||
* @param dataReceived Buffer to receive data from the device
|
||||
* @param size The length of the transaction, in bytes
|
||||
*/
|
||||
public int transaction(byte[] dataToSend, byte[] dataReceived, int size) {
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
|
||||
dataToSendBuffer.put(dataToSend);
|
||||
ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(size);
|
||||
int retVal = SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceivedBuffer, (byte) size);
|
||||
dataReceivedBuffer.get(dataReceived);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a simultaneous read/write transaction with the device.
|
||||
*
|
||||
* @param dataToSend The data to be written out to the device. Must be created using
|
||||
* ByteBuffer.allocateDirect().
|
||||
* @param dataReceived Buffer to receive data from the device. Must be created using
|
||||
* ByteBuffer.allocateDirect().
|
||||
* @param size The length of the transaction, in bytes
|
||||
*/
|
||||
public int transaction(ByteBuffer dataToSend, ByteBuffer dataReceived, int size) {
|
||||
if (!dataToSend.isDirect()) {
|
||||
throw new IllegalArgumentException("dataToSend must be a direct buffer");
|
||||
}
|
||||
if (dataToSend.capacity() < size) {
|
||||
throw new IllegalArgumentException("dataToSend is too small, must be at least " + size);
|
||||
}
|
||||
if (!dataReceived.isDirect()) {
|
||||
throw new IllegalArgumentException("dataReceived must be a direct buffer");
|
||||
}
|
||||
if (dataReceived.capacity() < size) {
|
||||
throw new IllegalArgumentException("dataReceived is too small, must be at least " + size);
|
||||
}
|
||||
return SPIJNI.spiTransaction(m_port, dataToSend, dataReceived, (byte) size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the accumulator.
|
||||
*
|
||||
* @param period Time between reads
|
||||
* @param cmd SPI command to send to request data
|
||||
* @param xferSize SPI transfer size, in bytes
|
||||
* @param validMask Mask to apply to received data for validity checking
|
||||
* @param validValue After validMask is applied, required matching value for validity checking
|
||||
* @param dataShift Bit shift to apply to received data to get actual data value
|
||||
* @param dataSize Size (in bits) of data field
|
||||
* @param isSigned Is data field signed?
|
||||
* @param bigEndian Is device big endian?
|
||||
*/
|
||||
public void initAccumulator(double period, int cmd, int xferSize,
|
||||
int validMask, int validValue,
|
||||
int dataShift, int dataSize,
|
||||
boolean isSigned, boolean bigEndian) {
|
||||
SPIJNI.spiInitAccumulator(m_port, (int) (period * 1.0e6), cmd,
|
||||
(byte) xferSize, validMask, validValue, (byte) dataShift,
|
||||
(byte) dataSize, isSigned, bigEndian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the accumulator.
|
||||
*/
|
||||
public void freeAccumulator() {
|
||||
SPIJNI.spiFreeAccumulator(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the accumulator to zero.
|
||||
*/
|
||||
public void resetAccumulator() {
|
||||
SPIJNI.spiResetAccumulator(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the center value of the accumulator.
|
||||
*
|
||||
* <p>The center value is subtracted from each value before it is added to the accumulator. This
|
||||
* is used for the center value of devices like gyros and accelerometers to make integration work
|
||||
* and to take the device offset into account when integrating.
|
||||
*/
|
||||
public void setAccumulatorCenter(int center) {
|
||||
SPIJNI.spiSetAccumulatorCenter(m_port, center);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the accumulator's deadband.
|
||||
*/
|
||||
public void setAccumulatorDeadband(int deadband) {
|
||||
SPIJNI.spiSetAccumulatorDeadband(m_port, deadband);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the last value read by the accumulator engine.
|
||||
*/
|
||||
public int getAccumulatorLastValue() {
|
||||
return SPIJNI.spiGetAccumulatorLastValue(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the accumulated value.
|
||||
*
|
||||
* @return The 64-bit value accumulated since the last Reset().
|
||||
*/
|
||||
public long getAccumulatorValue() {
|
||||
return SPIJNI.spiGetAccumulatorValue(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the number of accumulated values.
|
||||
*
|
||||
* <p>Read the count of the accumulated values since the accumulator was last Reset().
|
||||
*
|
||||
* @return The number of times samples from the channel were accumulated.
|
||||
*/
|
||||
public int getAccumulatorCount() {
|
||||
return SPIJNI.spiGetAccumulatorCount(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the average of the accumulated value.
|
||||
*
|
||||
* @return The accumulated average value (value / count).
|
||||
*/
|
||||
public double getAccumulatorAverage() {
|
||||
return SPIJNI.spiGetAccumulatorAverage(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the accumulated value and the number of accumulated values atomically.
|
||||
*
|
||||
* <p>This function reads the value and count atomically. This can be used for averaging.
|
||||
*
|
||||
* @param result AccumulatorResult object to store the results in.
|
||||
*/
|
||||
public void getAccumulatorOutput(AccumulatorResult result) {
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException("Null parameter `result'");
|
||||
}
|
||||
ByteBuffer value = ByteBuffer.allocateDirect(8);
|
||||
// set the byte order
|
||||
value.order(ByteOrder.LITTLE_ENDIAN);
|
||||
ByteBuffer count = ByteBuffer.allocateDirect(8);
|
||||
// set the byte order
|
||||
count.order(ByteOrder.LITTLE_ENDIAN);
|
||||
SPIJNI.spiGetAccumulatorOutput(m_port, value.asLongBuffer(), count.asLongBuffer());
|
||||
result.value = value.asLongBuffer().get(0);
|
||||
result.count = count.asLongBuffer().get(0);
|
||||
}
|
||||
}
|
||||
106
wpilibj/src/main/java/edu/wpi/first/wpilibj/SafePWM.java
Normal file
106
wpilibj/src/main/java/edu/wpi/first/wpilibj/SafePWM.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* Manages a PWM object.
|
||||
*/
|
||||
public class SafePWM extends PWM implements MotorSafety {
|
||||
|
||||
private final MotorSafetyHelper m_safetyHelper;
|
||||
|
||||
/**
|
||||
* Constructor for a SafePWM object taking a channel number.
|
||||
*
|
||||
* @param channel The channel number to be used for the underlying PWM object. 0-9 are on-board,
|
||||
* 10-19 are on the MXP port.
|
||||
*/
|
||||
public SafePWM(final int channel) {
|
||||
super(channel);
|
||||
|
||||
m_safetyHelper = new MotorSafetyHelper(this);
|
||||
m_safetyHelper.setExpiration(0.0);
|
||||
m_safetyHelper.setSafetyEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expiration time for the PWM object.
|
||||
*
|
||||
* @param timeout The timeout (in seconds) for this motor object
|
||||
*/
|
||||
public void setExpiration(double timeout) {
|
||||
m_safetyHelper.setExpiration(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the expiration time for the PWM object.
|
||||
*
|
||||
* @return The expiration time value.
|
||||
*/
|
||||
public double getExpiration() {
|
||||
return m_safetyHelper.getExpiration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the PWM object is currently alive or stopped due to a timeout.
|
||||
*
|
||||
* @return a bool value that is true if the motor has NOT timed out and should still be running.
|
||||
*/
|
||||
public boolean isAlive() {
|
||||
return m_safetyHelper.isAlive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the motor associated with this PWM object. This is called by the MotorSafetyHelper object
|
||||
* when it has a timeout for this PWM and needs to stop it from running.
|
||||
*/
|
||||
public void stopMotor() {
|
||||
disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if motor safety is enabled for this object.
|
||||
*
|
||||
* @return True if motor safety is enforced for this object
|
||||
*/
|
||||
public boolean isSafetyEnabled() {
|
||||
return m_safetyHelper.isSafetyEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed the MotorSafety timer. This method is called by the subclass motor whenever it updates its
|
||||
* speed, thereby resetting the timeout value.
|
||||
*
|
||||
* @deprecated Use {@link #feed()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("MethodName")
|
||||
public void Feed() {
|
||||
feed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed the MotorSafety timer. This method is called by the subclass motor whenever it updates its
|
||||
* speed, thereby resetting the timeout value.
|
||||
*/
|
||||
public void feed() {
|
||||
m_safetyHelper.feed();
|
||||
}
|
||||
|
||||
public void setSafetyEnabled(boolean enabled) {
|
||||
m_safetyHelper.setSafetyEnabled(enabled);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "PWM " + getChannel();
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
setDisabled();
|
||||
}
|
||||
}
|
||||
155
wpilibj/src/main/java/edu/wpi/first/wpilibj/SampleRobot.java
Normal file
155
wpilibj/src/main/java/edu/wpi/first/wpilibj/SampleRobot.java
Normal file
@@ -0,0 +1,155 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tInstances;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* A simple robot base class that knows the standard FRC competition states (disabled, autonomous,
|
||||
* or operator controlled).
|
||||
*
|
||||
* <p>You can build a simple robot program off of this by overriding the robotinit(), disabled(),
|
||||
* autonomous() and operatorControl() methods. The startCompetition() method will calls these
|
||||
* methods (sometimes repeatedly). depending on the state of the competition.
|
||||
*
|
||||
* <p>Alternatively you can override the robotMain() method and manage all aspects of the robot
|
||||
* yourself.
|
||||
*/
|
||||
public class SampleRobot extends RobotBase {
|
||||
|
||||
private boolean m_robotMainOverridden = true;
|
||||
|
||||
/**
|
||||
* Create a new SampleRobot.
|
||||
*/
|
||||
public SampleRobot() {
|
||||
robotInit();
|
||||
|
||||
// Tell the DS that the robot is ready to be enabled
|
||||
HAL.observeUserProgramStarting();
|
||||
|
||||
HAL.report(tResourceType.kResourceType_Framework, tInstances.kFramework_Simple);
|
||||
}
|
||||
|
||||
/**
|
||||
* Robot-wide initialization code should go here.
|
||||
*
|
||||
* <p>Users should override this method for default Robot-wide initialization which will be called
|
||||
* when the robot is first powered on. It will be called exactly one time.
|
||||
*
|
||||
* <p>Warning: the Driver Station "Robot Code" light and FMS "Robot Ready" indicators will be off
|
||||
* until RobotInit() exits. Code in RobotInit() that waits for enable will cause the robot to
|
||||
* never indicate that the code is ready, causing the robot to be bypassed in a match.
|
||||
*/
|
||||
protected void robotInit() {
|
||||
System.out.println("Default robotInit() method running, consider providing your own");
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabled should go here. Users should overload this method to run code that should run while
|
||||
* the field is disabled.
|
||||
*
|
||||
* <p>Called once each time the robot enters the disabled state.
|
||||
*/
|
||||
protected void disabled() {
|
||||
System.out.println("Default disabled() method running, consider providing your own");
|
||||
}
|
||||
|
||||
/**
|
||||
* Autonomous should go here. Users should add autonomous code to this method that should run
|
||||
* while the field is in the autonomous period.
|
||||
*
|
||||
* <p>Called once each time the robot enters the autonomous state.
|
||||
*/
|
||||
public void autonomous() {
|
||||
System.out.println("Default autonomous() method running, consider providing your own");
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator control (tele-operated) code should go here. Users should add Operator Control code to
|
||||
* this method that should run while the field is in the Operator Control (tele-operated) period.
|
||||
*
|
||||
* <p>Called once each time the robot enters the operator-controlled state.
|
||||
*/
|
||||
public void operatorControl() {
|
||||
System.out.println("Default operatorControl() method running, consider providing your own");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test code should go here. Users should add test code to this method that should run while the
|
||||
* robot is in test mode.
|
||||
*/
|
||||
@SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation")
|
||||
public void test() {
|
||||
System.out.println("Default test() method running, consider providing your own");
|
||||
}
|
||||
|
||||
/**
|
||||
* Robot main program for free-form programs.
|
||||
*
|
||||
* <p>This should be overridden by user subclasses if the intent is to not use the autonomous()
|
||||
* and operatorControl() methods. In that case, the program is responsible for sensing when to run
|
||||
* the autonomous and operator control functions in their program.
|
||||
*
|
||||
* <p>This method will be called immediately after the constructor is called. If it has not been
|
||||
* overridden by a user subclass (i.e. the default version runs), then the robotInit(),
|
||||
* disabled(), autonomous() and operatorControl() methods will be called.
|
||||
*/
|
||||
public void robotMain() {
|
||||
m_robotMainOverridden = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a competition. This code tracks the order of the field starting to ensure that everything
|
||||
* happens in the right order. Repeatedly run the correct method, either Autonomous or
|
||||
* OperatorControl when the robot is enabled. After running the correct method, wait for some
|
||||
* state to change, either the other mode starts or the robot is disabled. Then go back and wait
|
||||
* for the robot to be enabled again.
|
||||
*/
|
||||
public void startCompetition() {
|
||||
robotMain();
|
||||
if (!m_robotMainOverridden) {
|
||||
while (true) {
|
||||
if (isDisabled()) {
|
||||
m_ds.InDisabled(true);
|
||||
disabled();
|
||||
m_ds.InDisabled(false);
|
||||
while (isDisabled()) {
|
||||
Timer.delay(0.01);
|
||||
}
|
||||
} else if (isAutonomous()) {
|
||||
m_ds.InAutonomous(true);
|
||||
autonomous();
|
||||
m_ds.InAutonomous(false);
|
||||
while (isAutonomous() && !isDisabled()) {
|
||||
Timer.delay(0.01);
|
||||
}
|
||||
} else if (isTest()) {
|
||||
LiveWindow.setEnabled(true);
|
||||
m_ds.InTest(true);
|
||||
test();
|
||||
m_ds.InTest(false);
|
||||
while (isTest() && isEnabled()) {
|
||||
Timer.delay(0.01);
|
||||
}
|
||||
LiveWindow.setEnabled(false);
|
||||
} else {
|
||||
m_ds.InOperatorControl(true);
|
||||
operatorControl();
|
||||
m_ds.InOperatorControl(false);
|
||||
while (isOperatorControl() && !isDisabled()) {
|
||||
Timer.delay(0.01);
|
||||
}
|
||||
}
|
||||
} /* while loop */
|
||||
}
|
||||
}
|
||||
}
|
||||
38
wpilibj/src/main/java/edu/wpi/first/wpilibj/Sendable.java
Normal file
38
wpilibj/src/main/java/edu/wpi/first/wpilibj/Sendable.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.tables.ITable;
|
||||
|
||||
|
||||
/**
|
||||
* The base interface for objects that can be sent over the network through network tables.
|
||||
*/
|
||||
public interface Sendable {
|
||||
/**
|
||||
* Initializes a table for this {@link Sendable} object.
|
||||
*
|
||||
* @param subtable The table to put the values in.
|
||||
*/
|
||||
void initTable(ITable subtable);
|
||||
|
||||
/**
|
||||
* The table that is associated with this {@link Sendable}.
|
||||
*
|
||||
* @return the table that is currently associated with the {@link Sendable}.
|
||||
*/
|
||||
ITable getTable();
|
||||
|
||||
/**
|
||||
* The string representation of the named data type that will be used by the smart dashboard for
|
||||
* this {@link Sendable}.
|
||||
*
|
||||
* @return The type of this {@link Sendable}.
|
||||
*/
|
||||
String getSmartDashboardType();
|
||||
}
|
||||
248
wpilibj/src/main/java/edu/wpi/first/wpilibj/SensorBase.java
Normal file
248
wpilibj/src/main/java/edu/wpi/first/wpilibj/SensorBase.java
Normal file
@@ -0,0 +1,248 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.AnalogJNI;
|
||||
import edu.wpi.first.wpilibj.hal.ConstantsJNI;
|
||||
import edu.wpi.first.wpilibj.hal.DIOJNI;
|
||||
import edu.wpi.first.wpilibj.hal.PDPJNI;
|
||||
import edu.wpi.first.wpilibj.hal.PWMJNI;
|
||||
import edu.wpi.first.wpilibj.hal.PortsJNI;
|
||||
import edu.wpi.first.wpilibj.hal.RelayJNI;
|
||||
import edu.wpi.first.wpilibj.hal.SolenoidJNI;
|
||||
|
||||
/**
|
||||
* Base class for all sensors. Stores most recent status information as well as containing utility
|
||||
* functions for checking channels and error processing.
|
||||
*/
|
||||
public abstract class SensorBase {
|
||||
/**
|
||||
* Ticks per microsecond.
|
||||
*/
|
||||
public static final int kSystemClockTicksPerMicrosecond =
|
||||
ConstantsJNI.getSystemClockTicksPerMicrosecond();
|
||||
/**
|
||||
* Number of digital channels per roboRIO.
|
||||
*/
|
||||
public static final int kDigitalChannels = PortsJNI.getNumDigitalChannels();
|
||||
/**
|
||||
* Number of analog input channels per roboRIO.
|
||||
*/
|
||||
public static final int kAnalogInputChannels = PortsJNI.getNumAnalogInputs();
|
||||
/**
|
||||
* Number of analog output channels per roboRIO.
|
||||
*/
|
||||
public static final int kAnalogOutputChannels = PortsJNI.getNumAnalogOutputs();
|
||||
/**
|
||||
* Number of solenoid channels per module.
|
||||
*/
|
||||
public static final int kSolenoidChannels = PortsJNI.getNumSolenoidChannels();
|
||||
/**
|
||||
* Number of PWM channels per roboRIO.
|
||||
*/
|
||||
public static final int kPwmChannels = PortsJNI.getNumPWMChannels();
|
||||
/**
|
||||
* Number of relay channels per roboRIO.
|
||||
*/
|
||||
public static final int kRelayChannels = PortsJNI.getNumRelayHeaders();
|
||||
/**
|
||||
* Number of power distribution channels per PDP.
|
||||
*/
|
||||
public static final int kPDPChannels = PortsJNI.getNumPDPChannels();
|
||||
/**
|
||||
* Number of power distribution modules per PDP.
|
||||
*/
|
||||
public static final int kPDPModules = PortsJNI.getNumPDPModules();
|
||||
/**
|
||||
* Number of PCM Modules.
|
||||
*/
|
||||
public static final int kPCMModules = PortsJNI.getNumPCMModules();
|
||||
|
||||
private static int m_defaultSolenoidModule = 0;
|
||||
|
||||
/**
|
||||
* Creates an instance of the sensor base and gets an FPGA handle.
|
||||
*/
|
||||
public SensorBase() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default location for the Solenoid module.
|
||||
*
|
||||
* @param moduleNumber The number of the solenoid module to use.
|
||||
*/
|
||||
public static void setDefaultSolenoidModule(final int moduleNumber) {
|
||||
checkSolenoidModule(moduleNumber);
|
||||
SensorBase.m_defaultSolenoidModule = moduleNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the solenoid module is correct.
|
||||
*
|
||||
* @param moduleNumber The solenoid module module number to check.
|
||||
*/
|
||||
protected static void checkSolenoidModule(final int moduleNumber) {
|
||||
if (!SolenoidJNI.checkSolenoidModule(moduleNumber)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested solenoid module is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kPCMModules)
|
||||
.append(", Requested: ")
|
||||
.append(moduleNumber);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the digital channel number is valid. Verify that the channel number is one of the
|
||||
* legal channel numbers. Channel numbers are 0-based.
|
||||
*
|
||||
* @param channel The channel number to check.
|
||||
*/
|
||||
protected static void checkDigitalChannel(final int channel) {
|
||||
if (!DIOJNI.checkDIOChannel(channel)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested DIO channel is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kDigitalChannels)
|
||||
.append(", Requested: ")
|
||||
.append(channel);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the digital channel number is valid. Verify that the channel number is one of the
|
||||
* legal channel numbers. Channel numbers are 0-based.
|
||||
*
|
||||
* @param channel The channel number to check.
|
||||
*/
|
||||
protected static void checkRelayChannel(final int channel) {
|
||||
if (!RelayJNI.checkRelayChannel(channel)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested relay channel is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kRelayChannels)
|
||||
.append(", Requested: ")
|
||||
.append(channel);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the digital channel number is valid. Verify that the channel number is one of the
|
||||
* legal channel numbers. Channel numbers are 0-based.
|
||||
*
|
||||
* @param channel The channel number to check.
|
||||
*/
|
||||
protected static void checkPWMChannel(final int channel) {
|
||||
if (!PWMJNI.checkPWMChannel(channel)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested PWM channel is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kPwmChannels)
|
||||
.append(", Requested: ")
|
||||
.append(channel);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the analog input number is value. Verify that the analog input number is one of the
|
||||
* legal channel numbers. Channel numbers are 0-based.
|
||||
*
|
||||
* @param channel The channel number to check.
|
||||
*/
|
||||
protected static void checkAnalogInputChannel(final int channel) {
|
||||
if (!AnalogJNI.checkAnalogInputChannel(channel)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested analog input channel is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kAnalogInputChannels)
|
||||
.append(", Requested: ")
|
||||
.append(channel);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the analog input number is value. Verify that the analog input number is one of the
|
||||
* legal channel numbers. Channel numbers are 0-based.
|
||||
*
|
||||
* @param channel The channel number to check.
|
||||
*/
|
||||
protected static void checkAnalogOutputChannel(final int channel) {
|
||||
if (!AnalogJNI.checkAnalogOutputChannel(channel)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested analog output channel is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kAnalogOutputChannels)
|
||||
.append(", Requested: ")
|
||||
.append(channel);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the solenoid channel number is within limits. Channel numbers are 0-based.
|
||||
*
|
||||
* @param channel The channel number to check.
|
||||
*/
|
||||
protected static void checkSolenoidChannel(final int channel) {
|
||||
if (!SolenoidJNI.checkSolenoidChannel(channel)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested solenoid channel is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kSolenoidChannels)
|
||||
.append(", Requested: ")
|
||||
.append(channel);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the power distribution channel number is within limits. Channel numbers are
|
||||
* 0-based.
|
||||
*
|
||||
* @param channel The channel number to check.
|
||||
*/
|
||||
protected static void checkPDPChannel(final int channel) {
|
||||
if (!PDPJNI.checkPDPChannel(channel)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested PDP channel is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kPDPChannels)
|
||||
.append(", Requested: ")
|
||||
.append(channel);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the PDP module number is within limits. module numbers are 0-based.
|
||||
*
|
||||
* @param module The module number to check.
|
||||
*/
|
||||
protected static void checkPDPModule(final int module) {
|
||||
if (!PDPJNI.checkPDPModule(module)) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Requested PDP module is out of range. Minimum: 0, Maximum: ")
|
||||
.append(kPDPModules)
|
||||
.append(", Requested: ")
|
||||
.append(module);
|
||||
throw new IndexOutOfBoundsException(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of the default solenoid module.
|
||||
*
|
||||
* @return The number of the default solenoid module.
|
||||
*/
|
||||
public static int getDefaultSolenoidModule() {
|
||||
return SensorBase.m_defaultSolenoidModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the resources used by this object.
|
||||
*/
|
||||
public void free() {
|
||||
}
|
||||
}
|
||||
356
wpilibj/src/main/java/edu/wpi/first/wpilibj/SerialPort.java
Normal file
356
wpilibj/src/main/java/edu/wpi/first/wpilibj/SerialPort.java
Normal file
@@ -0,0 +1,356 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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 java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.hal.SerialPortJNI;
|
||||
|
||||
/**
|
||||
* Driver for the RS-232 serial port on the roboRIO.
|
||||
*
|
||||
* <p>The current implementation uses the VISA formatted I/O mode. This means that all traffic goes
|
||||
* through the formatted buffers. This allows the intermingled use of print(), readString(), and the
|
||||
* raw buffer accessors read() and write().
|
||||
*
|
||||
* <p>More information can be found in the NI-VISA User Manual here: http://www.ni
|
||||
* .com/pdf/manuals/370423a.pdf and the NI-VISA Programmer's Reference Manual here:
|
||||
* http://www.ni.com/pdf/manuals/370132c.pdf
|
||||
*/
|
||||
public class SerialPort {
|
||||
|
||||
private byte m_port;
|
||||
|
||||
public enum Port {
|
||||
kOnboard(0), kMXP(1), kUSB(2), kUSB1(2), kUSB2(3);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public int value;
|
||||
|
||||
private Port(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the parity to use for serial communications.
|
||||
*/
|
||||
public enum Parity {
|
||||
kNone(0), kOdd(1), kEven(2), kMark(3), kSpace(4);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private Parity(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the number of stop bits to use for Serial Communication.
|
||||
*/
|
||||
public enum StopBits {
|
||||
kOne(10), kOnePointFive(15), kTwo(20);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private StopBits(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents what type of flow control to use for serial communication.
|
||||
*/
|
||||
public enum FlowControl {
|
||||
kNone(0), kXonXoff(1), kRtsCts(2), kDtsDsr(4);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private FlowControl(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents which type of buffer mode to use when writing to a serial m_port.
|
||||
*/
|
||||
public enum WriteBufferMode {
|
||||
kFlushOnAccess(1), kFlushWhenFull(2);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public final int value;
|
||||
|
||||
private WriteBufferMode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a Serial Port class.
|
||||
*
|
||||
* @param baudRate The baud rate to configure the serial port.
|
||||
* @param port The Serial port to use
|
||||
* @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
|
||||
* @param parity Select the type of parity checking to use.
|
||||
* @param stopBits The number of stop bits to use as defined by the enum StopBits.
|
||||
*/
|
||||
public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity,
|
||||
StopBits stopBits) {
|
||||
m_port = (byte) port.value;
|
||||
|
||||
SerialPortJNI.serialInitializePort(m_port);
|
||||
SerialPortJNI.serialSetBaudRate(m_port, baudRate);
|
||||
SerialPortJNI.serialSetDataBits(m_port, (byte) dataBits);
|
||||
SerialPortJNI.serialSetParity(m_port, (byte) parity.value);
|
||||
SerialPortJNI.serialSetStopBits(m_port, (byte) stopBits.value);
|
||||
|
||||
// Set the default read buffer size to 1 to return bytes immediately
|
||||
setReadBufferSize(1);
|
||||
|
||||
// Set the default timeout to 5 seconds.
|
||||
setTimeout(5.0);
|
||||
|
||||
// Don't wait until the buffer is full to transmit.
|
||||
setWriteBufferMode(WriteBufferMode.kFlushOnAccess);
|
||||
|
||||
disableTermination();
|
||||
|
||||
HAL.report(tResourceType.kResourceType_SerialPort, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a Serial Port class. Defaults to one stop bit.
|
||||
*
|
||||
* @param baudRate The baud rate to configure the serial port.
|
||||
* @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
|
||||
* @param parity Select the type of parity checking to use.
|
||||
*/
|
||||
public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity) {
|
||||
this(baudRate, port, dataBits, parity, StopBits.kOne);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a Serial Port class. Defaults to no parity and one stop bit.
|
||||
*
|
||||
* @param baudRate The baud rate to configure the serial port.
|
||||
* @param dataBits The number of data bits per transfer. Valid values are between 5 and 8 bits.
|
||||
*/
|
||||
public SerialPort(final int baudRate, Port port, final int dataBits) {
|
||||
this(baudRate, port, dataBits, Parity.kNone, StopBits.kOne);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a Serial Port class. Defaults to 8 databits, no parity, and one stop
|
||||
* bit.
|
||||
*
|
||||
* @param baudRate The baud rate to configure the serial port.
|
||||
*/
|
||||
public SerialPort(final int baudRate, Port port) {
|
||||
this(baudRate, port, 8, Parity.kNone, StopBits.kOne);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
public void free() {
|
||||
SerialPortJNI.serialClose(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of flow control to enable on this port.
|
||||
*
|
||||
* <p>By default, flow control is disabled.
|
||||
*
|
||||
* @param flowControl the FlowControl m_value to use
|
||||
*/
|
||||
public void setFlowControl(FlowControl flowControl) {
|
||||
SerialPortJNI.serialSetFlowControl(m_port, (byte) flowControl.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable termination and specify the termination character.
|
||||
*
|
||||
* <p>Termination is currently only implemented for receive. When the the terminator is received,
|
||||
* the read() or readString() will return fewer bytes than requested, stopping after the
|
||||
* terminator.
|
||||
*
|
||||
* @param terminator The character to use for termination.
|
||||
*/
|
||||
public void enableTermination(char terminator) {
|
||||
SerialPortJNI.serialEnableTermination(m_port, terminator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable termination with the default terminator '\n'
|
||||
*
|
||||
* <p>Termination is currently only implemented for receive. When the the terminator is received,
|
||||
* the read() or readString() will return fewer bytes than requested, stopping after the
|
||||
* terminator.
|
||||
*
|
||||
* <p>The default terminator is '\n'
|
||||
*/
|
||||
public void enableTermination() {
|
||||
enableTermination('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable termination behavior.
|
||||
*/
|
||||
public void disableTermination() {
|
||||
SerialPortJNI.serialDisableTermination(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes currently available to read from the serial port.
|
||||
*
|
||||
* @return The number of bytes available to read.
|
||||
*/
|
||||
public int getBytesReceived() {
|
||||
return SerialPortJNI.serialGetBytesRecieved(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a string out of the buffer. Reads the entire contents of the buffer
|
||||
*
|
||||
* @return The read string
|
||||
*/
|
||||
public String readString() {
|
||||
return readString(getBytesReceived());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a string out of the buffer. Reads the entire contents of the buffer
|
||||
*
|
||||
* @param count the number of characters to read into the string
|
||||
* @return The read string
|
||||
*/
|
||||
public String readString(int count) {
|
||||
byte[] out = read(count);
|
||||
try {
|
||||
return new String(out, 0, out.length, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
ex.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read raw bytes out of the buffer.
|
||||
*
|
||||
* @param count The maximum number of bytes to read.
|
||||
* @return An array of the read bytes
|
||||
*/
|
||||
public byte[] read(final int count) {
|
||||
ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(count);
|
||||
int gotten = SerialPortJNI.serialRead(m_port, dataReceivedBuffer, count);
|
||||
byte[] retVal = new byte[gotten];
|
||||
dataReceivedBuffer.get(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write raw bytes to the serial port.
|
||||
*
|
||||
* @param buffer The buffer of bytes to write.
|
||||
* @param count The maximum number of bytes to write.
|
||||
* @return The number of bytes actually written into the port.
|
||||
*/
|
||||
public int write(byte[] buffer, int count) {
|
||||
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(count);
|
||||
dataToSendBuffer.put(buffer, 0, count);
|
||||
return SerialPortJNI.serialWrite(m_port, dataToSendBuffer, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a string to the serial port.
|
||||
*
|
||||
* @param data The string to write to the serial port.
|
||||
* @return The number of bytes actually written into the port.
|
||||
*/
|
||||
public int writeString(String data) {
|
||||
return write(data.getBytes(), data.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the timeout of the serial m_port.
|
||||
*
|
||||
* <p>This defines the timeout for transactions with the hardware. It will affect reads if less
|
||||
* bytes are available than the read buffer size (defaults to 1) and very large writes.
|
||||
*
|
||||
* @param timeout The number of seconds to to wait for I/O.
|
||||
*/
|
||||
public void setTimeout(double timeout) {
|
||||
SerialPortJNI.serialSetTimeout(m_port, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the size of the input buffer.
|
||||
*
|
||||
* <p>Specify the amount of data that can be stored before data from the device is returned to
|
||||
* Read. If you want data that is received to be returned immediately, set this to 1.
|
||||
*
|
||||
* <p>It the buffer is not filled before the read timeout expires, all data that has been received
|
||||
* so far will be returned.
|
||||
*
|
||||
* @param size The read buffer size.
|
||||
*/
|
||||
public void setReadBufferSize(int size) {
|
||||
SerialPortJNI.serialSetReadBufferSize(m_port, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the size of the output buffer.
|
||||
*
|
||||
* <p>Specify the amount of data that can be stored before being transmitted to the device.
|
||||
*
|
||||
* @param size The write buffer size.
|
||||
*/
|
||||
public void setWriteBufferSize(int size) {
|
||||
SerialPortJNI.serialSetWriteBufferSize(m_port, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the flushing behavior of the output buffer.
|
||||
*
|
||||
* <p>When set to kFlushOnAccess, data is synchronously written to the serial port after each
|
||||
* call to either print() or write().
|
||||
*
|
||||
* <p>When set to kFlushWhenFull, data will only be written to the serial port when the buffer
|
||||
* is full or when flush() is called.
|
||||
*
|
||||
* @param mode The write buffer mode.
|
||||
*/
|
||||
public void setWriteBufferMode(WriteBufferMode mode) {
|
||||
SerialPortJNI.serialSetWriteMode(m_port, (byte) mode.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the output buffer to be written to the port.
|
||||
*
|
||||
* <p>This is used when setWriteBufferMode() is set to kFlushWhenFull to force a flush before the
|
||||
* buffer is full.
|
||||
*/
|
||||
public void flush() {
|
||||
SerialPortJNI.serialFlush(m_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the serial port driver to a known state.
|
||||
*
|
||||
* <p>Empty the transmit and receive buffers in the device and formatted I/O.
|
||||
*/
|
||||
public void reset() {
|
||||
SerialPortJNI.serialClear(m_port);
|
||||
}
|
||||
}
|
||||
144
wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java
Normal file
144
wpilibj/src/main/java/edu/wpi/first/wpilibj/Servo.java
Normal file
@@ -0,0 +1,144 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
/**
|
||||
* Standard hobby style servo.
|
||||
*
|
||||
* <p>The range parameters default to the appropriate values for the Hitec HS-322HD servo provided
|
||||
* in the FIRST Kit of Parts in 2008.
|
||||
*/
|
||||
public class Servo extends PWM {
|
||||
|
||||
private static final double kMaxServoAngle = 180.0;
|
||||
private static final double kMinServoAngle = 0.0;
|
||||
|
||||
protected static final double kDefaultMaxServoPWM = 2.4;
|
||||
protected static final double kDefaultMinServoPWM = .6;
|
||||
|
||||
/**
|
||||
* Constructor.<br>
|
||||
*
|
||||
* <p>By default {@value #kDefaultMaxServoPWM} ms is used as the maxPWM value<br> By default
|
||||
* {@value #kDefaultMinServoPWM} ms is used as the minPWM value<br>
|
||||
*
|
||||
* @param channel The PWM channel to which the servo is attached. 0-9 are on-board, 10-19 are on
|
||||
* the MXP port
|
||||
*/
|
||||
public Servo(final int channel) {
|
||||
super(channel);
|
||||
setBounds(kDefaultMaxServoPWM, 0, 0, 0, kDefaultMinServoPWM);
|
||||
setPeriodMultiplier(PeriodMultiplier.k4X);
|
||||
|
||||
LiveWindow.addActuator("Servo", getChannel(), this);
|
||||
HAL.report(tResourceType.kResourceType_Servo, getChannel());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the servo position.
|
||||
*
|
||||
* <p>Servo values range from 0.0 to 1.0 corresponding to the range of full left to full right.
|
||||
*
|
||||
* @param value Position from 0.0 to 1.0.
|
||||
*/
|
||||
public void set(double value) {
|
||||
setPosition(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the servo position.
|
||||
*
|
||||
* <p>Servo values range from 0.0 to 1.0 corresponding to the range of full left to full right.
|
||||
*
|
||||
* @return Position from 0.0 to 1.0.
|
||||
*/
|
||||
public double get() {
|
||||
return getPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the servo angle.
|
||||
*
|
||||
* <p>Assume that the servo angle is linear with respect to the PWM value (big assumption, need to
|
||||
* test).
|
||||
*
|
||||
* <p>Servo angles that are out of the supported range of the servo simply "saturate" in that
|
||||
* direction In other words, if the servo has a range of (X degrees to Y degrees) than angles of
|
||||
* less than X result in an angle of X being set and angles of more than Y degrees result in an
|
||||
* angle of Y being set.
|
||||
*
|
||||
* @param degrees The angle in degrees to set the servo.
|
||||
*/
|
||||
public void setAngle(double degrees) {
|
||||
if (degrees < kMinServoAngle) {
|
||||
degrees = kMinServoAngle;
|
||||
} else if (degrees > kMaxServoAngle) {
|
||||
degrees = kMaxServoAngle;
|
||||
}
|
||||
|
||||
setPosition(((degrees - kMinServoAngle)) / getServoAngleRange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the servo angle.
|
||||
*
|
||||
* <p>Assume that the servo angle is linear with respect to the PWM value (big assumption, need to
|
||||
* test).
|
||||
*
|
||||
* @return The angle in degrees to which the servo is set.
|
||||
*/
|
||||
public double getAngle() {
|
||||
return getPosition() * getServoAngleRange() + kMinServoAngle;
|
||||
}
|
||||
|
||||
private double getServoAngleRange() {
|
||||
return kMaxServoAngle - kMinServoAngle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
public String getSmartDashboardType() {
|
||||
return "Servo";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
private ITableListener m_tableListener;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
m_tableListener = (source, key, value, isNew) -> set((double) value);
|
||||
m_table.addTableListener("Value", m_tableListener, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
// TODO: Broken, should only remove the listener from "Value" only.
|
||||
m_table.removeTableListener(m_tableListener);
|
||||
}
|
||||
}
|
||||
141
wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java
Normal file
141
wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.hal.SolenoidJNI;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
/**
|
||||
* Solenoid class for running high voltage Digital Output on the PCM.
|
||||
*
|
||||
* <p>The Solenoid class is typically used for pneumatic solenoids, but could be used for any
|
||||
* device within the current spec of the PCM.
|
||||
*/
|
||||
public class Solenoid extends SolenoidBase implements LiveWindowSendable {
|
||||
|
||||
private final int m_channel; // The channel to control.
|
||||
private int m_solenoidHandle;
|
||||
|
||||
/**
|
||||
* Constructor using the default PCM ID (defaults to 0).
|
||||
*
|
||||
* @param channel The channel on the PCM to control (0..7).
|
||||
*/
|
||||
public Solenoid(final int channel) {
|
||||
this(getDefaultSolenoidModule(), channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param moduleNumber The CAN ID of the PCM the solenoid is attached to.
|
||||
* @param channel The channel on the PCM to control (0..7).
|
||||
*/
|
||||
public Solenoid(final int moduleNumber, final int channel) {
|
||||
super(moduleNumber);
|
||||
m_channel = channel;
|
||||
|
||||
checkSolenoidModule(m_moduleNumber);
|
||||
checkSolenoidChannel(m_channel);
|
||||
|
||||
int portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) m_channel);
|
||||
m_solenoidHandle = SolenoidJNI.initializeSolenoidPort(portHandle);
|
||||
|
||||
LiveWindow.addActuator("Solenoid", m_moduleNumber, m_channel, this);
|
||||
HAL.report(tResourceType.kResourceType_Solenoid, m_channel, m_moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
public synchronized void free() {
|
||||
SolenoidJNI.freeSolenoidPort(m_solenoidHandle);
|
||||
m_solenoidHandle = 0;
|
||||
if (m_table != null) {
|
||||
m_table.removeTableListener(m_tableListener);
|
||||
}
|
||||
super.free();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a solenoid.
|
||||
*
|
||||
* @param on True will turn the solenoid output on. False will turn the solenoid output off.
|
||||
*/
|
||||
public void set(boolean on) {
|
||||
SolenoidJNI.setSolenoid(m_solenoidHandle, on);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the current value of the solenoid.
|
||||
*
|
||||
* @return True if the solenoid output is on or false if the solenoid output is off.
|
||||
*/
|
||||
public boolean get() {
|
||||
return SolenoidJNI.getSolenoid(m_solenoidHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if solenoid is blacklisted. If a solenoid is shorted, it is added to the blacklist and
|
||||
* disabled until power cycle, or until faults are cleared.
|
||||
*
|
||||
* @return If solenoid is disabled due to short.
|
||||
* @see #clearAllPCMStickyFaults()
|
||||
*/
|
||||
public boolean isBlackListed() {
|
||||
int value = getPCMSolenoidBlackList() & (1 << m_channel);
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
public String getSmartDashboardType() {
|
||||
return "Solenoid";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
private ITableListener m_tableListener;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putBoolean("Value", get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
set(false); // Stop for safety
|
||||
m_tableListener = (source, key, value, isNew) -> set((boolean) value);
|
||||
m_table.addTableListener("Value", m_tableListener, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
set(false); // Stop for safety
|
||||
// TODO: Broken, should only remove the listener from "Value" only.
|
||||
m_table.removeTableListener(m_tableListener);
|
||||
}
|
||||
}
|
||||
142
wpilibj/src/main/java/edu/wpi/first/wpilibj/SolenoidBase.java
Normal file
142
wpilibj/src/main/java/edu/wpi/first/wpilibj/SolenoidBase.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.SolenoidJNI;
|
||||
|
||||
/**
|
||||
* SolenoidBase class is the common base class for the {@link Solenoid} and {@link DoubleSolenoid}
|
||||
* classes.
|
||||
*/
|
||||
public abstract class SolenoidBase extends SensorBase {
|
||||
|
||||
protected final int m_moduleNumber; // The number of the solenoid module being used.
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param moduleNumber The PCM CAN ID
|
||||
*/
|
||||
public SolenoidBase(final int moduleNumber) {
|
||||
m_moduleNumber = moduleNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all 8 solenoids from the specified module as a single byte.
|
||||
*
|
||||
* @param moduleNumber the module number to read
|
||||
* @return The current value of all 8 solenoids on the module.
|
||||
*/
|
||||
public static int getAll(int moduleNumber) {
|
||||
return SolenoidJNI.getAllSolenoids(moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all 8 solenoids from the module used by this solenoid as a single byte.
|
||||
*
|
||||
* @return The current value of all 8 solenoids on this module.
|
||||
*/
|
||||
public int getAll() {
|
||||
return SolenoidBase.getAll(m_moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads complete solenoid blacklist for all 8 solenoids as a single byte. If a solenoid is
|
||||
* shorted, it is added to the blacklist and disabled until power cycle, or until faults are
|
||||
* cleared.
|
||||
*
|
||||
* @param moduleNumber the module number to read
|
||||
* @return The solenoid blacklist of all 8 solenoids on the module.
|
||||
* @see #clearAllPCMStickyFaults()
|
||||
*/
|
||||
public static int getPCMSolenoidBlackList(int moduleNumber) {
|
||||
return SolenoidJNI.getPCMSolenoidBlackList(moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads complete solenoid blacklist for all 8 solenoids as a single byte. If a solenoid is
|
||||
* shorted, it is added to the blacklist and disabled until power cycle, or until faults are
|
||||
* cleared.
|
||||
*
|
||||
* @return The solenoid blacklist of all 8 solenoids on the module.
|
||||
* @see #clearAllPCMStickyFaults()
|
||||
*/
|
||||
public int getPCMSolenoidBlackList() {
|
||||
return SolenoidBase.getPCMSolenoidBlackList(m_moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the common highside solenoid voltage rail is too low, most likely a solenoid channel
|
||||
* is shorted.
|
||||
*
|
||||
* @param moduleNumber the module number to read
|
||||
* @return true if PCM sticky fault is set
|
||||
*/
|
||||
public static boolean getPCMSolenoidVoltageStickyFault(int moduleNumber) {
|
||||
return SolenoidJNI.getPCMSolenoidVoltageStickyFault(moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the common highside solenoid voltage rail is too low, most likely a solenoid channel
|
||||
* is shorted.
|
||||
*
|
||||
* @return true if PCM sticky fault is set
|
||||
*/
|
||||
public boolean getPCMSolenoidVoltageStickyFault() {
|
||||
return SolenoidBase.getPCMSolenoidVoltageStickyFault(m_moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* The common highside solenoid voltage rail is too low, most likely a solenoid channel is
|
||||
* shorted.
|
||||
*
|
||||
* @param moduleNumber the module number to read
|
||||
* @return true if PCM is in fault state.
|
||||
*/
|
||||
public static boolean getPCMSolenoidVoltageFault(int moduleNumber) {
|
||||
return SolenoidJNI.getPCMSolenoidVoltageFault(moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* The common highside solenoid voltage rail is too low, most likely a solenoid channel is
|
||||
* shorted.
|
||||
*
|
||||
* @return true if PCM is in fault state.
|
||||
*/
|
||||
public boolean getPCMSolenoidVoltageFault() {
|
||||
return SolenoidBase.getPCMSolenoidVoltageFault(m_moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear ALL sticky faults inside PCM that Compressor is wired to.
|
||||
*
|
||||
* <p>If a sticky fault is set, then it will be persistently cleared. Compressor drive maybe
|
||||
* momentarily disable while flags are being cleared. Care should be taken to not call this too
|
||||
* frequently, otherwise normal compressor functionality may be prevented.
|
||||
*
|
||||
* <p>If no sticky faults are set then this call will have no effect.
|
||||
*
|
||||
* @param moduleNumber the module number to read
|
||||
*/
|
||||
public static void clearAllPCMStickyFaults(int moduleNumber) {
|
||||
SolenoidJNI.clearAllPCMStickyFaults(moduleNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear ALL sticky faults inside PCM that Compressor is wired to.
|
||||
*
|
||||
* <p>If a sticky fault is set, then it will be persistently cleared. Compressor drive maybe
|
||||
* momentarily disable while flags are being cleared. Care should be taken to not call this too
|
||||
* frequently, otherwise normal compressor functionality may be prevented.
|
||||
*
|
||||
* <p>If no sticky faults are set then this call will have no effect.
|
||||
*/
|
||||
public void clearAllPCMStickyFaults() {
|
||||
SolenoidBase.clearAllPCMStickyFaults(m_moduleNumber);
|
||||
}
|
||||
}
|
||||
52
wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java
Normal file
52
wpilibj/src/main/java/edu/wpi/first/wpilibj/Spark.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* REV Robotics SPARK Speed Controller.
|
||||
*/
|
||||
public class Spark extends PWMSpeedController {
|
||||
|
||||
/**
|
||||
* Common initialization code called by all constructors.
|
||||
*
|
||||
* <p>Note that the SPARK uses the following bounds for PWM values. These values should work
|
||||
* reasonably well for most controllers, but if users experience issues such as asymmetric
|
||||
* behavior around the deadband or inability to saturate the controller in either direction,
|
||||
* calibration is recommended. The calibration procedure can be found in the Spark User Manual
|
||||
* available from REV Robotics.
|
||||
*
|
||||
* <p>- 2.003ms = full "forward" - 1.55ms = the "high end" of the deadband range - 1.50ms =
|
||||
* center of the deadband range (off) - 1.46ms = the "low end" of the deadband range - .999ms =
|
||||
* full "reverse"
|
||||
*/
|
||||
protected void initSpark() {
|
||||
setBounds(2.003, 1.55, 1.50, 1.46, .999);
|
||||
setPeriodMultiplier(PeriodMultiplier.k1X);
|
||||
setSpeed(0.0);
|
||||
setZeroLatch();
|
||||
|
||||
LiveWindow.addActuator("Spark", getChannel(), this);
|
||||
HAL.report(tResourceType.kResourceType_RevSPARK, getChannel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param channel The PWM channel that the SPARK is attached to. 0-9 are on-board, 10-19 are on
|
||||
* the MXP port
|
||||
*/
|
||||
public Spark(final int channel) {
|
||||
super(channel);
|
||||
initSpark();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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;
|
||||
|
||||
/**
|
||||
* Interface for speed controlling devices.
|
||||
*/
|
||||
public interface SpeedController extends PIDOutput {
|
||||
/**
|
||||
* Common interface for setting the speed of a speed controller.
|
||||
*
|
||||
* @param speed The speed to set. Value should be between -1.0 and 1.0.
|
||||
*/
|
||||
void set(double speed);
|
||||
|
||||
/**
|
||||
* Common interface for getting the current set speed of a speed controller.
|
||||
*
|
||||
* @return The current set speed. Value is between -1.0 and 1.0.
|
||||
*/
|
||||
double get();
|
||||
|
||||
/**
|
||||
* Common interface for inverting direction of a speed controller.
|
||||
*
|
||||
* @param isInverted The state of inversion true is inverted.
|
||||
*/
|
||||
void setInverted(boolean isInverted);
|
||||
|
||||
/**
|
||||
* Common interface for returning if a speed controller is in the inverted state or not.
|
||||
*
|
||||
* @return isInverted The state of the inversion true is inverted.
|
||||
*/
|
||||
boolean getInverted();
|
||||
|
||||
/**
|
||||
* Disable the speed controller.
|
||||
*/
|
||||
void disable();
|
||||
|
||||
/**
|
||||
* Stops motor movement. Motor can be moved again by calling set without having to re-enable the
|
||||
* motor.
|
||||
*/
|
||||
void stopMotor();
|
||||
}
|
||||
46
wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java
Normal file
46
wpilibj/src/main/java/edu/wpi/first/wpilibj/Talon.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* Cross the Road Electronics (CTRE) Talon and Talon SR Speed Controller.
|
||||
*/
|
||||
public class Talon extends PWMSpeedController {
|
||||
|
||||
/**
|
||||
* Constructor for a Talon (original or Talon SR).
|
||||
*
|
||||
* <p>Note that the Talon uses the following bounds for PWM values. These values should work
|
||||
* reasonably well for most controllers, but if users experience issues such as asymmetric
|
||||
* behavior around the deadband or inability to saturate the controller in either direction,
|
||||
* calibration is recommended. The calibration procedure can be found in the Talon User Manual
|
||||
* available from CTRE.
|
||||
*
|
||||
* <p>- 2.037ms = full "forward" - 1.539ms = the "high end" of the deadband range - 1.513ms =
|
||||
* center of the deadband range (off) - 1.487ms = the "low end" of the deadband range - .989ms
|
||||
* = full "reverse"
|
||||
*
|
||||
* @param channel The PWM channel that the Talon is attached to. 0-9 are on-board, 10-19 are on
|
||||
* the MXP port
|
||||
*/
|
||||
public Talon(final int channel) {
|
||||
super(channel);
|
||||
|
||||
setBounds(2.037, 1.539, 1.513, 1.487, .989);
|
||||
setPeriodMultiplier(PeriodMultiplier.k1X);
|
||||
setSpeed(0.0);
|
||||
setZeroLatch();
|
||||
|
||||
LiveWindow.addActuator("Talon", getChannel(), this);
|
||||
HAL.report(tResourceType.kResourceType_Talon, getChannel());
|
||||
}
|
||||
}
|
||||
47
wpilibj/src/main/java/edu/wpi/first/wpilibj/TalonSRX.java
Normal file
47
wpilibj/src/main/java/edu/wpi/first/wpilibj/TalonSRX.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* Cross the Road Electronics (CTRE) Talon SRX Speed Controller with PWM control.
|
||||
*/
|
||||
public class TalonSRX extends PWMSpeedController {
|
||||
|
||||
/**
|
||||
* Constructor for a TalonSRX connected via PWM.
|
||||
*
|
||||
* <p>Note that the TalonSRX uses the following bounds for PWM values. These values should work
|
||||
* reasonably well for most controllers, but if users experience issues such as asymmetric
|
||||
* behavior around the deadband or inability to saturate the controller in either direction,
|
||||
* calibration is recommended. The calibration procedure can be found in the TalonSRX User Manual
|
||||
* available from CTRE.
|
||||
*
|
||||
* <p>- 2.0004ms = full "forward" - 1.52ms = the "high end" of the deadband range - 1.50ms =
|
||||
* center
|
||||
* of the deadband range (off) - 1.48ms = the "low end" of the deadband range - .997ms = full
|
||||
* "reverse"
|
||||
*
|
||||
* @param channel The PWM channel that the TalonSRX is attached to. 0-9 are on-board, 10-19 are
|
||||
* on the MXP port
|
||||
*/
|
||||
public TalonSRX(final int channel) {
|
||||
super(channel);
|
||||
|
||||
setBounds(2.004, 1.52, 1.50, 1.48, .997);
|
||||
setPeriodMultiplier(PeriodMultiplier.k1X);
|
||||
setSpeed(0.0);
|
||||
setZeroLatch();
|
||||
|
||||
LiveWindow.addActuator("TalonSRX", getChannel(), this);
|
||||
HAL.report(tResourceType.kResourceType_TalonSRX, getChannel());
|
||||
}
|
||||
}
|
||||
43
wpilibj/src/main/java/edu/wpi/first/wpilibj/Threads.java
Normal file
43
wpilibj/src/main/java/edu/wpi/first/wpilibj/Threads.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.ThreadsJNI;
|
||||
|
||||
public class Threads {
|
||||
|
||||
/**
|
||||
* Get the thread priority for the current thread.
|
||||
* @return The current thread priority. Scaled 1-99.
|
||||
*/
|
||||
public static int getCurrentThreadPriority() {
|
||||
return ThreadsJNI.getCurrentThreadPriority();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the current thread is realtime.
|
||||
* @return If the current thread is realtime
|
||||
*/
|
||||
public static boolean getCurrentThreadIsRealTime() {
|
||||
return ThreadsJNI.getCurrentThreadIsRealTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thread priority for the current thread.
|
||||
*
|
||||
* @param realTime Set to true to set a realtime priority, false for standard
|
||||
* priority
|
||||
* @param priority Priority to set the thread to. Scaled 1-99, with 1 being
|
||||
* highest. On RoboRIO, priority is ignored for non realtime setting
|
||||
*
|
||||
* @return The success state of setting the priority
|
||||
*/
|
||||
public static boolean setCurrentThreadPriority(boolean realTime, int priority) {
|
||||
return ThreadsJNI.setCurrentThreadPriority(realTime, priority);
|
||||
}
|
||||
}
|
||||
66
wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java
Normal file
66
wpilibj/src/main/java/edu/wpi/first/wpilibj/TimedRobot.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. 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.hal.FRCNetComm.tInstances;
|
||||
import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
|
||||
/**
|
||||
* TimedRobot implements the IterativeRobotBase robot program framework.
|
||||
*
|
||||
* <p>The TimedRobot class is intended to be subclassed by a user creating a robot program.
|
||||
*
|
||||
* <p>periodic() functions from the base class are called on an interval by a Notifier instance.
|
||||
*/
|
||||
public class TimedRobot extends IterativeRobotBase {
|
||||
public static final double DEFAULT_PERIOD = 0.02;
|
||||
|
||||
private double m_period = DEFAULT_PERIOD;
|
||||
|
||||
// Prevents loop from starting if user calls setPeriod() in robotInit()
|
||||
private boolean m_startLoop = false;
|
||||
|
||||
private Notifier m_loop = new Notifier(() -> {
|
||||
loopFunc();
|
||||
});
|
||||
|
||||
public TimedRobot() {
|
||||
// HAL.report(tResourceType.kResourceType_Framework, tInstances.kFramework_Periodic);
|
||||
HAL.report(tResourceType.kResourceType_Framework, tInstances.kFramework_Iterative);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an alternate "main loop" via startCompetition().
|
||||
*/
|
||||
public void startCompetition() {
|
||||
// loop forever, calling the appropriate mode-dependent function
|
||||
m_startLoop = true;
|
||||
m_loop.startPeriodic(m_period);
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(1000 * 60 * 60 * 24);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time period between calls to Periodic() functions.
|
||||
*
|
||||
* @param period Period in seconds.
|
||||
*/
|
||||
public void setPeriod(double period) {
|
||||
m_period = period;
|
||||
|
||||
if (m_startLoop) {
|
||||
m_loop.startPeriodic(m_period);
|
||||
}
|
||||
}
|
||||
}
|
||||
179
wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java
Normal file
179
wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java
Normal file
@@ -0,0 +1,179 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.util.BaseSystemNotInitializedException;
|
||||
|
||||
public class Timer {
|
||||
private static StaticInterface impl;
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
public static void SetImplementation(StaticInterface ti) {
|
||||
impl = ti;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the system clock time in seconds. Return the time from the FPGA hardware clock in
|
||||
* seconds since the FPGA started.
|
||||
*
|
||||
* @return Robot running time in seconds.
|
||||
*/
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public static double getFPGATimestamp() {
|
||||
if (impl != null) {
|
||||
return impl.getFPGATimestamp();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(StaticInterface.class, Timer.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the approximate match time The FMS does not currently send the official match time to
|
||||
* the robots This returns the time since the enable signal sent from the Driver Station At the
|
||||
* beginning of autonomous, the time is reset to 0.0 seconds At the beginning of teleop, the time
|
||||
* is reset to +15.0 seconds If the robot is disabled, this returns 0.0 seconds Warning: This is
|
||||
* not an official time (so it cannot be used to argue with referees).
|
||||
*
|
||||
* @return Match time in seconds since the beginning of autonomous
|
||||
*/
|
||||
public static double getMatchTime() {
|
||||
if (impl != null) {
|
||||
return impl.getMatchTime();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(StaticInterface.class, Timer.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the thread for a specified time. Pause the execution of the thread for a specified period
|
||||
* of time given in seconds. Motors will continue to run at their last assigned values, and
|
||||
* sensors will continue to update. Only the task containing the wait will pause until the wait
|
||||
* time is expired.
|
||||
*
|
||||
* @param seconds Length of time to pause
|
||||
*/
|
||||
public static void delay(final double seconds) {
|
||||
if (impl != null) {
|
||||
impl.delay(seconds);
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(StaticInterface.class, Timer.class);
|
||||
}
|
||||
}
|
||||
|
||||
public interface StaticInterface {
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
double getFPGATimestamp();
|
||||
|
||||
double getMatchTime();
|
||||
|
||||
void delay(final double seconds);
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
Interface newTimer();
|
||||
}
|
||||
|
||||
private final Interface m_timer;
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
public Timer() {
|
||||
if (impl != null) {
|
||||
m_timer = impl.newTimer();
|
||||
} else {
|
||||
throw new BaseSystemNotInitializedException(StaticInterface.class, Timer.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current time from the timer. If the clock is running it is derived from the current
|
||||
* system clock the start time stored in the timer class. If the clock is not running, then return
|
||||
* the time when it was last stopped.
|
||||
*
|
||||
* @return Current time value for this timer in seconds
|
||||
*/
|
||||
public double get() {
|
||||
return m_timer.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the timer by setting the time to 0. Make the timer startTime the current time so new
|
||||
* requests will be relative now
|
||||
*/
|
||||
public void reset() {
|
||||
m_timer.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the timer running. Just set the running flag to true indicating that all time requests
|
||||
* should be relative to the system clock.
|
||||
*/
|
||||
public void start() {
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the timer. This computes the time as of now and clears the running flag, causing all
|
||||
* subsequent time requests to be read from the accumulated time rather than looking at the system
|
||||
* clock.
|
||||
*/
|
||||
public void stop() {
|
||||
m_timer.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the period specified has passed and if it has, advance the start time by that period.
|
||||
* This is useful to decide if it's time to do periodic work without drifting later by the time it
|
||||
* took to get around to checking.
|
||||
*
|
||||
* @param period The period to check for (in seconds).
|
||||
* @return If the period has passed.
|
||||
*/
|
||||
public boolean hasPeriodPassed(double period) {
|
||||
return m_timer.hasPeriodPassed(period);
|
||||
}
|
||||
|
||||
public interface Interface {
|
||||
/**
|
||||
* Get the current time from the timer. If the clock is running it is derived from the current
|
||||
* system clock the start time stored in the timer class. If the clock is not running, then
|
||||
* return the time when it was last stopped.
|
||||
*
|
||||
* @return Current time value for this timer in seconds
|
||||
*/
|
||||
double get();
|
||||
|
||||
/**
|
||||
* Reset the timer by setting the time to 0. Make the timer startTime the current time so new
|
||||
* requests will be relative now
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Start the timer running. Just set the running flag to true indicating that all time requests
|
||||
* should be relative to the system clock.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop the timer. This computes the time as of now and clears the running flag, causing all
|
||||
* subsequent time requests to be read from the accumulated time rather than looking at the
|
||||
* system clock.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
|
||||
/**
|
||||
* Check if the period specified has passed and if it has, advance the start time by that
|
||||
* period. This is useful to decide if it's time to do periodic work without drifting later by
|
||||
* the time it took to get around to checking.
|
||||
*
|
||||
* @param period The period to check for (in seconds).
|
||||
* @return If the period has passed.
|
||||
*/
|
||||
boolean hasPeriodPassed(double period);
|
||||
}
|
||||
}
|
||||
428
wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java
Normal file
428
wpilibj/src/main/java/edu/wpi/first/wpilibj/Ultrasonic.java
Normal file
@@ -0,0 +1,428 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Ultrasonic rangefinder class. The Ultrasonic rangefinder measures absolute distance based on the
|
||||
* round-trip time of a ping generated by the controller. These sensors use two transducers, a
|
||||
* speaker and a microphone both tuned to the ultrasonic range. A common ultrasonic sensor, the
|
||||
* Daventech SRF04 requires a short pulse to be generated on a digital channel. This causes the
|
||||
* chirp to be emmitted. A second line becomes high as the ping is transmitted and goes low when the
|
||||
* echo is received. The time that the line is high determines the round trip distance (time of
|
||||
* flight).
|
||||
*/
|
||||
public class Ultrasonic extends SensorBase implements PIDSource, LiveWindowSendable {
|
||||
|
||||
/**
|
||||
* The units to return when PIDGet is called.
|
||||
*/
|
||||
public enum Unit {
|
||||
/**
|
||||
* Use inches for PIDGet.
|
||||
*/
|
||||
kInches,
|
||||
/**
|
||||
* Use millimeters for PIDGet.
|
||||
*/
|
||||
kMillimeters
|
||||
}
|
||||
|
||||
// Time (sec) for the ping trigger pulse.
|
||||
private static final double kPingTime = 10 * 1e-6;
|
||||
private static final double kSpeedOfSoundInchesPerSec = 1130.0 * 12.0;
|
||||
// head of the ultrasonic sensor list
|
||||
private static Ultrasonic m_firstSensor = null;
|
||||
// automatic round robin mode
|
||||
private static boolean m_automaticEnabled = false;
|
||||
private DigitalInput m_echoChannel;
|
||||
private DigitalOutput m_pingChannel = null;
|
||||
private boolean m_allocatedChannels;
|
||||
private boolean m_enabled = false;
|
||||
private Counter m_counter = null;
|
||||
private Ultrasonic m_nextSensor = null;
|
||||
// task doing the round-robin automatic sensing
|
||||
private static Thread m_task = null;
|
||||
private Unit m_units;
|
||||
private static int m_instances = 0;
|
||||
protected PIDSourceType m_pidSource = PIDSourceType.kDisplacement;
|
||||
|
||||
/**
|
||||
* Background task that goes through the list of ultrasonic sensors and pings each one in turn.
|
||||
* The counter is configured to read the timing of the returned echo pulse.
|
||||
*
|
||||
* <p><b>DANGER WILL ROBINSON, DANGER WILL ROBINSON:</b> This code runs as a task and assumes that
|
||||
* none of the ultrasonic sensors will change while it's running. If one does, then this will
|
||||
* certainly break. Make sure to disable automatic mode before changing anything with the
|
||||
* sensors!!
|
||||
*/
|
||||
private class UltrasonicChecker extends Thread {
|
||||
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
Ultrasonic ultrasonic = null;
|
||||
while (m_automaticEnabled) {
|
||||
if (ultrasonic == null) {
|
||||
ultrasonic = m_firstSensor;
|
||||
}
|
||||
if (ultrasonic == null) {
|
||||
return;
|
||||
}
|
||||
if (ultrasonic.isEnabled()) {
|
||||
// Do the ping
|
||||
ultrasonic.m_pingChannel.pulse(kPingTime);
|
||||
}
|
||||
ultrasonic = ultrasonic.m_nextSensor;
|
||||
Timer.delay(.1); // wait for ping to return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Ultrasonic Sensor. This is the common code that initializes the ultrasonic
|
||||
* sensor given that there are two digital I/O channels allocated. If the system was running in
|
||||
* automatic mode (round robin) when the new sensor is added, it is stopped, the sensor is added,
|
||||
* then automatic mode is restored.
|
||||
*/
|
||||
private synchronized void initialize() {
|
||||
if (m_task == null) {
|
||||
m_task = new UltrasonicChecker();
|
||||
}
|
||||
final boolean originalMode = m_automaticEnabled;
|
||||
setAutomaticMode(false); // kill task when adding a new sensor
|
||||
m_nextSensor = m_firstSensor;
|
||||
m_firstSensor = this;
|
||||
|
||||
m_counter = new Counter(m_echoChannel); // set up counter for this
|
||||
// sensor
|
||||
m_counter.setMaxPeriod(1.0);
|
||||
m_counter.setSemiPeriodMode(true);
|
||||
m_counter.reset();
|
||||
m_enabled = true; // make it available for round robin scheduling
|
||||
setAutomaticMode(originalMode);
|
||||
|
||||
m_instances++;
|
||||
HAL.report(tResourceType.kResourceType_Ultrasonic, m_instances);
|
||||
LiveWindow.addSensor("Ultrasonic", m_echoChannel.getChannel(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the Ultrasonic Sensor. This is designed to supchannel the Daventech SRF04
|
||||
* and Vex ultrasonic sensors.
|
||||
*
|
||||
* @param pingChannel The digital output channel that sends the pulse to initiate the sensor
|
||||
* sending the ping.
|
||||
* @param echoChannel The digital input channel that receives the echo. The length of time that
|
||||
* the echo is high represents the round trip time of the ping, and the
|
||||
* distance.
|
||||
* @param units The units returned in either kInches or kMilliMeters
|
||||
*/
|
||||
public Ultrasonic(final int pingChannel, final int echoChannel, Unit units) {
|
||||
m_pingChannel = new DigitalOutput(pingChannel);
|
||||
m_echoChannel = new DigitalInput(echoChannel);
|
||||
m_allocatedChannels = true;
|
||||
m_units = units;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the Ultrasonic Sensor. This is designed to supchannel the Daventech SRF04
|
||||
* and Vex ultrasonic sensors. Default unit is inches.
|
||||
*
|
||||
* @param pingChannel The digital output channel that sends the pulse to initiate the sensor
|
||||
* sending the ping.
|
||||
* @param echoChannel The digital input channel that receives the echo. The length of time that
|
||||
* the echo is high represents the round trip time of the ping, and the
|
||||
* distance.
|
||||
*/
|
||||
public Ultrasonic(final int pingChannel, final int echoChannel) {
|
||||
this(pingChannel, echoChannel, Unit.kInches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo channel and a
|
||||
* DigitalOutput for the ping channel.
|
||||
*
|
||||
* @param pingChannel The digital output object that starts the sensor doing a ping. Requires a
|
||||
* 10uS pulse to start.
|
||||
* @param echoChannel The digital input object that times the return pulse to determine the
|
||||
* range.
|
||||
* @param units The units returned in either kInches or kMilliMeters
|
||||
*/
|
||||
public Ultrasonic(DigitalOutput pingChannel, DigitalInput echoChannel, Unit units) {
|
||||
requireNonNull(pingChannel, "Provided ping channel was null");
|
||||
requireNonNull(echoChannel, "Provided echo channel was null");
|
||||
|
||||
m_allocatedChannels = false;
|
||||
m_pingChannel = pingChannel;
|
||||
m_echoChannel = echoChannel;
|
||||
m_units = units;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of an Ultrasonic Sensor from a DigitalInput for the echo channel and a
|
||||
* DigitalOutput for the ping channel. Default unit is inches.
|
||||
*
|
||||
* @param pingChannel The digital output object that starts the sensor doing a ping. Requires a
|
||||
* 10uS pulse to start.
|
||||
* @param echoChannel The digital input object that times the return pulse to determine the
|
||||
* range.
|
||||
*/
|
||||
public Ultrasonic(DigitalOutput pingChannel, DigitalInput echoChannel) {
|
||||
this(pingChannel, echoChannel, Unit.kInches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor for the ultrasonic sensor. Delete the instance of the ultrasonic sensor by freeing
|
||||
* the allocated digital channels. If the system was in automatic mode (round robin), then it is
|
||||
* stopped, then started again after this sensor is removed (provided this wasn't the last
|
||||
* sensor).
|
||||
*/
|
||||
@Override
|
||||
public synchronized void free() {
|
||||
final boolean wasAutomaticMode = m_automaticEnabled;
|
||||
setAutomaticMode(false);
|
||||
if (m_allocatedChannels) {
|
||||
if (m_pingChannel != null) {
|
||||
m_pingChannel.free();
|
||||
}
|
||||
if (m_echoChannel != null) {
|
||||
m_echoChannel.free();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_counter != null) {
|
||||
m_counter.free();
|
||||
m_counter = null;
|
||||
}
|
||||
|
||||
m_pingChannel = null;
|
||||
m_echoChannel = null;
|
||||
|
||||
if (this == m_firstSensor) {
|
||||
m_firstSensor = m_nextSensor;
|
||||
if (m_firstSensor == null) {
|
||||
setAutomaticMode(false);
|
||||
}
|
||||
} else {
|
||||
for (Ultrasonic s = m_firstSensor; s != null; s = s.m_nextSensor) {
|
||||
if (this == s.m_nextSensor) {
|
||||
s.m_nextSensor = s.m_nextSensor.m_nextSensor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_firstSensor != null && wasAutomaticMode) {
|
||||
setAutomaticMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn Automatic mode on/off. When in Automatic mode, all sensors will fire in round robin,
|
||||
* waiting a set time between each sensor.
|
||||
*
|
||||
* @param enabling Set to true if round robin scheduling should start for all the ultrasonic
|
||||
* sensors. This scheduling method assures that the sensors are non-interfering
|
||||
* because no two sensors fire at the same time. If another scheduling algorithm
|
||||
* is preffered, it can be implemented by pinging the sensors manually and waiting
|
||||
* for the results to come back.
|
||||
*/
|
||||
public void setAutomaticMode(boolean enabling) {
|
||||
if (enabling == m_automaticEnabled) {
|
||||
return; // ignore the case of no change
|
||||
}
|
||||
m_automaticEnabled = enabling;
|
||||
|
||||
if (enabling) {
|
||||
/* Clear all the counters so no data is valid. No synchronization is
|
||||
* needed because the background task is stopped.
|
||||
*/
|
||||
for (Ultrasonic u = m_firstSensor; u != null; u = u.m_nextSensor) {
|
||||
u.m_counter.reset();
|
||||
}
|
||||
|
||||
// Start round robin task
|
||||
m_task.start();
|
||||
} else {
|
||||
// Wait for background task to stop running
|
||||
try {
|
||||
m_task.join();
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
/* Clear all the counters (data now invalid) since automatic mode is
|
||||
* disabled. No synchronization is needed because the background task is
|
||||
* stopped.
|
||||
*/
|
||||
for (Ultrasonic u = m_firstSensor; u != null; u = u.m_nextSensor) {
|
||||
u.m_counter.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Single ping to ultrasonic sensor. Send out a single ping to the ultrasonic sensor. This only
|
||||
* works if automatic (round robin) mode is disabled. A single ping is sent out, and the counter
|
||||
* should count the semi-period when it comes in. The counter is reset to make the current value
|
||||
* invalid.
|
||||
*/
|
||||
public void ping() {
|
||||
setAutomaticMode(false); // turn off automatic round robin if pinging
|
||||
// single sensor
|
||||
m_counter.reset(); // reset the counter to zero (invalid data now)
|
||||
// do the ping to start getting a single range
|
||||
m_pingChannel.pulse(kPingTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is a valid range measurement. The ranges are accumulated in a counter that will
|
||||
* increment on each edge of the echo (return) signal. If the count is not at least 2, then the
|
||||
* range has not yet been measured, and is invalid.
|
||||
*
|
||||
* @return true if the range is valid
|
||||
*/
|
||||
public boolean isRangeValid() {
|
||||
return m_counter.get() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the range in inches from the ultrasonic sensor. If there is no valid value yet, i.e. at
|
||||
* least one measurement hasn't completed, then return 0.
|
||||
*
|
||||
* @return double Range in inches of the target returned from the ultrasonic sensor.
|
||||
*/
|
||||
public double getRangeInches() {
|
||||
if (isRangeValid()) {
|
||||
return m_counter.getPeriod() * kSpeedOfSoundInchesPerSec / 2.0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the range in millimeters from the ultrasonic sensor. If there is no valid value yet, i.e.
|
||||
* at least one measurement hasn't completed, then return 0.
|
||||
*
|
||||
* @return double Range in millimeters of the target returned by the ultrasonic sensor.
|
||||
*/
|
||||
public double getRangeMM() {
|
||||
return getRangeInches() * 25.4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
if (!pidSource.equals(PIDSourceType.kDisplacement)) {
|
||||
throw new IllegalArgumentException("Only displacement PID is allowed for ultrasonics.");
|
||||
}
|
||||
m_pidSource = pidSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return m_pidSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the range in the current DistanceUnit for the PIDSource base object.
|
||||
*
|
||||
* @return The range in DistanceUnit
|
||||
*/
|
||||
@Override
|
||||
public double pidGet() {
|
||||
switch (m_units) {
|
||||
case kInches:
|
||||
return getRangeInches();
|
||||
case kMillimeters:
|
||||
return getRangeMM();
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current DistanceUnit that should be used for the PIDSource base object.
|
||||
*
|
||||
* @param units The DistanceUnit that should be used.
|
||||
*/
|
||||
public void setDistanceUnits(Unit units) {
|
||||
m_units = units;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current DistanceUnit that is used for the PIDSource base object.
|
||||
*
|
||||
* @return The type of DistanceUnit that is being used.
|
||||
*/
|
||||
public Unit getDistanceUnits() {
|
||||
return m_units;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the ultrasonic enabled.
|
||||
*
|
||||
* @return true if the ultrasonic is enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the ultrasonic is enabled.
|
||||
*
|
||||
* @param enable set to true to enable the ultrasonic
|
||||
*/
|
||||
public void setEnabled(boolean enable) {
|
||||
m_enabled = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Live Window code, only does anything if live window is activated.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Ultrasonic";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable subtable) {
|
||||
m_table = subtable;
|
||||
updateTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTable() {
|
||||
if (m_table != null) {
|
||||
m_table.putNumber("Value", getRangeInches());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLiveWindowMode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLiveWindowMode() {
|
||||
}
|
||||
}
|
||||
59
wpilibj/src/main/java/edu/wpi/first/wpilibj/Utility.java
Normal file
59
wpilibj/src/main/java/edu/wpi/first/wpilibj/Utility.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.HALUtil;
|
||||
|
||||
/**
|
||||
* Contains global utility functions.
|
||||
*/
|
||||
public final class Utility {
|
||||
|
||||
private Utility() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the FPGA Version number. For now, expect this to be 2009.
|
||||
*
|
||||
* @return FPGA Version number.
|
||||
*/
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
int getFPGAVersion() {
|
||||
return HALUtil.getFPGAVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the FPGA Revision number. The format of the revision is 3 numbers. The 12 most
|
||||
* significant bits are the Major Revision. the next 8 bits are the Minor Revision. The 12 least
|
||||
* significant bits are the Build Number.
|
||||
*
|
||||
* @return FPGA Revision number.
|
||||
*/
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
long getFPGARevision() {
|
||||
return (long) HALUtil.getFPGARevision();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the microsecond timer from the FPGA.
|
||||
*
|
||||
* @return The current time in microseconds according to the FPGA.
|
||||
*/
|
||||
public static long getFPGATime() {
|
||||
return HALUtil.getFPGATime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of the "USER" button on the roboRIO.
|
||||
*
|
||||
* @return true if the button is currently pressed down
|
||||
*/
|
||||
public static boolean getUserButton() {
|
||||
return HALUtil.getFPGAButton();
|
||||
}
|
||||
}
|
||||
48
wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java
Normal file
48
wpilibj/src/main/java/edu/wpi/first/wpilibj/Victor.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* VEX Robotics Victor 888 Speed Controller The Vex Robotics Victor 884 Speed Controller can also
|
||||
* be used with this class but may need to be calibrated per the Victor 884 user manual.
|
||||
*/
|
||||
public class Victor extends PWMSpeedController {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* <p>Note that the Victor uses the following bounds for PWM values. These values were determined
|
||||
* empirically and optimized for the Victor 888. These values should work reasonably well for
|
||||
* Victor 884 controllers also but if users experience issues such as asymmetric behaviour around
|
||||
* the deadband or inability to saturate the controller in either direction, calibration is
|
||||
* recommended. The calibration procedure can be found in the Victor 884 User Manual available
|
||||
* from VEX Robotics: http://content.vexrobotics.com/docs/ifi-v884-users-manual-9-25-06.pdf
|
||||
*
|
||||
* <p>- 2.027ms = full "forward" - 1.525ms = the "high end" of the deadband range - 1.507ms =
|
||||
* center of the deadband range (off) - 1.49ms = the "low end" of the deadband range - 1.026ms =
|
||||
* full "reverse"
|
||||
*
|
||||
* @param channel The PWM channel that the Victor is attached to. 0-9 are
|
||||
* on-board, 10-19 are on the MXP port
|
||||
*/
|
||||
public Victor(final int channel) {
|
||||
super(channel);
|
||||
|
||||
setBounds(2.027, 1.525, 1.507, 1.49, 1.026);
|
||||
setPeriodMultiplier(PeriodMultiplier.k2X);
|
||||
setSpeed(0.0);
|
||||
setZeroLatch();
|
||||
|
||||
LiveWindow.addActuator("Victor", getChannel(), this);
|
||||
HAL.report(tResourceType.kResourceType_Victor, getChannel());
|
||||
}
|
||||
}
|
||||
46
wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java
Normal file
46
wpilibj/src/main/java/edu/wpi/first/wpilibj/VictorSP.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
|
||||
|
||||
/**
|
||||
* VEX Robotics Victor SP Speed Controller.
|
||||
*/
|
||||
public class VictorSP extends PWMSpeedController {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* <p>Note that the VictorSP uses the following bounds for PWM values. These values should work
|
||||
* reasonably well for most controllers, but if users experience issues such as asymmetric
|
||||
* behavior around the deadband or inability to saturate the controller in either direction,
|
||||
* calibration is recommended. The calibration procedure can be found in the VictorSP User Manual
|
||||
* available from CTRE.
|
||||
*
|
||||
* <p>- 2.004ms = full "forward" - 1.52ms = the "high end" of the deadband range - 1.50ms =
|
||||
* center of the deadband range (off) - 1.48ms = the "low end" of the deadband range - .997ms =
|
||||
* full "reverse"
|
||||
*
|
||||
* @param channel The PWM channel that the VictorSP is attached to. 0-9 are
|
||||
* on-board, 10-19 are on the MXP port
|
||||
*/
|
||||
public VictorSP(final int channel) {
|
||||
super(channel);
|
||||
|
||||
setBounds(2.004, 1.52, 1.50, 1.48, .997);
|
||||
setPeriodMultiplier(PeriodMultiplier.k1X);
|
||||
setSpeed(0.0);
|
||||
setZeroLatch();
|
||||
|
||||
LiveWindow.addActuator("VictorSP", getChannel(), this);
|
||||
HAL.report(tResourceType.kResourceType_VictorSP, getChannel());
|
||||
}
|
||||
}
|
||||
262
wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java
Normal file
262
wpilibj/src/main/java/edu/wpi/first/wpilibj/XboxController.java
Normal file
@@ -0,0 +1,262 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.hal.FRCNetComm.tResourceType;
|
||||
import edu.wpi.first.wpilibj.hal.HAL;
|
||||
|
||||
/**
|
||||
* Handle input from Xbox 360 or Xbox One controllers connected to the Driver Station.
|
||||
*
|
||||
* <p>This class handles Xbox input that comes from the Driver Station. Each time a value is
|
||||
* requested the most recent value is returend. There is a single class instance for each controller
|
||||
* and the mapping of ports to hardware buttons depends on the code in the Driver Station.
|
||||
*/
|
||||
public class XboxController extends GamepadBase {
|
||||
private DriverStation m_ds;
|
||||
private int m_outputs;
|
||||
private short m_leftRumble;
|
||||
private short m_rightRumble;
|
||||
|
||||
/**
|
||||
* Construct an instance of a joystick. The joystick index is the USB port on the drivers
|
||||
* station.
|
||||
*
|
||||
* @param port The port on the Driver Station that the joystick is plugged into.
|
||||
*/
|
||||
public XboxController(final int port) {
|
||||
super(port);
|
||||
m_ds = DriverStation.getInstance();
|
||||
|
||||
// HAL.report(tResourceType.kResourceType_XboxController, port);
|
||||
HAL.report(tResourceType.kResourceType_Joystick, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the X axis value of the controller.
|
||||
*
|
||||
* @param hand Side of controller whose value should be returned.
|
||||
* @return The X axis value of the controller.
|
||||
*/
|
||||
@Override
|
||||
public double getX(Hand hand) {
|
||||
if (hand.equals(Hand.kLeft)) {
|
||||
return getRawAxis(0);
|
||||
} else {
|
||||
return getRawAxis(4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Y axis value of the controller.
|
||||
*
|
||||
* @param hand Side of controller whose value should be returned.
|
||||
* @return The Y axis value of the controller.
|
||||
*/
|
||||
@Override
|
||||
public double getY(Hand hand) {
|
||||
if (hand.equals(Hand.kLeft)) {
|
||||
return getRawAxis(1);
|
||||
} else {
|
||||
return getRawAxis(5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the axis.
|
||||
*
|
||||
* @param axis The axis to read, starting at 0.
|
||||
* @return The value of the axis.
|
||||
*/
|
||||
public double getRawAxis(final int axis) {
|
||||
return m_ds.getStickAxis(getPort(), axis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of the bumper button on the controller.
|
||||
*
|
||||
* @param hand Side of controller whose value should be returned.
|
||||
* @return The state of the button.
|
||||
*/
|
||||
@Override
|
||||
public boolean getBumper(Hand hand) {
|
||||
if (hand.equals(Hand.kLeft)) {
|
||||
return getRawButton(5);
|
||||
} else {
|
||||
return getRawButton(6);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not supported for the XboxController. This method is only here to complete the
|
||||
* GenericHID interface.
|
||||
*
|
||||
* @param hand This parameter is ignored for the Joystick class and is only here to complete the
|
||||
* GenericHID interface.
|
||||
* @return The state of the trigger (always false)
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public boolean getTrigger(Hand hand) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not supported for the XboxController. This method is only here to complete the
|
||||
* GenericHID interface.
|
||||
*
|
||||
* @param hand This parameter is ignored for the Joystick class and is only here to complete the
|
||||
* GenericHID interface.
|
||||
* @return The state of the top button (always false)
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
public boolean getTop(Hand hand) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the button value (starting at button 1).
|
||||
*
|
||||
* <p>The appropriate button is returned as a boolean value.
|
||||
*
|
||||
* @param button The button number to be read (starting at 1).
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getRawButton(final int button) {
|
||||
return m_ds.getStickButton(getPort(), (byte) button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trigger axis value of the controller.
|
||||
*
|
||||
* @param hand Side of controller whose value should be returned.
|
||||
* @return The trigger axis value of the controller.
|
||||
*/
|
||||
public double getTriggerAxis(Hand hand) {
|
||||
if (hand.equals(Hand.kLeft)) {
|
||||
return getRawAxis(2);
|
||||
} else {
|
||||
return getRawAxis(3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of the A button on the controller.
|
||||
*
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getAButton() {
|
||||
return getRawButton(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of the B button on the controller.
|
||||
*
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getBButton() {
|
||||
return getRawButton(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of the X button on the controller.
|
||||
*
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getXButton() {
|
||||
return getRawButton(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of the Y button on the controller.
|
||||
*
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getYButton() {
|
||||
return getRawButton(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of the stick button on the controller.
|
||||
*
|
||||
* @param hand Side of controller whose value should be returned.
|
||||
* @return The state of the button.
|
||||
*/
|
||||
@Override
|
||||
public boolean getStickButton(Hand hand) {
|
||||
if (hand.equals(Hand.kLeft)) {
|
||||
return getRawButton(9);
|
||||
} else {
|
||||
return getRawButton(10);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of the back button on the controller.
|
||||
*
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getBackButton() {
|
||||
return getRawButton(7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value of the start button on the controller.
|
||||
*
|
||||
* @return The state of the button.
|
||||
*/
|
||||
public boolean getStartButton() {
|
||||
return getRawButton(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPOV(int pov) {
|
||||
return m_ds.getStickPOV(getPort(), pov);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPOVCount() {
|
||||
return m_ds.getStickPOVCount(getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HIDType getType() {
|
||||
return HIDType.values()[m_ds.getJoystickType(getPort())];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return m_ds.getJoystickName(getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutput(int outputNumber, boolean value) {
|
||||
m_outputs = (m_outputs & ~(1 << (outputNumber - 1))) | ((value ? 1 : 0) << (outputNumber - 1));
|
||||
HAL.setJoystickOutputs((byte) getPort(), m_outputs, m_leftRumble, m_rightRumble);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutputs(int value) {
|
||||
m_outputs = value;
|
||||
HAL.setJoystickOutputs((byte) getPort(), m_outputs, m_leftRumble, m_rightRumble);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRumble(RumbleType type, double value) {
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
} else if (value > 1) {
|
||||
value = 1;
|
||||
}
|
||||
if (type == RumbleType.kLeftRumble) {
|
||||
m_leftRumble = (short) (value * 65535);
|
||||
} else {
|
||||
m_rightRumble = (short) (value * 65535);
|
||||
}
|
||||
HAL.setJoystickOutputs((byte) getPort(), m_outputs, m_leftRumble, m_rightRumble);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.buttons;
|
||||
|
||||
import edu.wpi.first.wpilibj.command.Command;
|
||||
|
||||
/**
|
||||
* This class provides an easy way to link commands to OI inputs.
|
||||
*
|
||||
* <p>It is very easy to link a button to a command. For instance, you could link the trigger button
|
||||
* of a joystick to a "score" command.
|
||||
*
|
||||
* <p>This class represents a subclass of Trigger that is specifically aimed at buttons on an
|
||||
* operator interface as a common use case of the more generalized Trigger objects. This is a simple
|
||||
* wrapper around Trigger with the method names renamed to fit the Button object use.
|
||||
*/
|
||||
public abstract class Button extends Trigger {
|
||||
|
||||
/**
|
||||
* Starts the given command whenever the button is newly pressed.
|
||||
*
|
||||
* @param command the command to start
|
||||
*/
|
||||
public void whenPressed(final Command command) {
|
||||
whenActive(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constantly starts the given command while the button is held.
|
||||
*
|
||||
* {@link Command#start()} will be called repeatedly while the button is held, and will be
|
||||
* canceled when the button is released.
|
||||
*
|
||||
* @param command the command to start
|
||||
*/
|
||||
public void whileHeld(final Command command) {
|
||||
whileActive(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the command when the button is released.
|
||||
*
|
||||
* @param command the command to start
|
||||
*/
|
||||
public void whenReleased(final Command command) {
|
||||
whenInactive(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the command whenever the button is pressed (on then off then on).
|
||||
*
|
||||
* @param command the command to start
|
||||
*/
|
||||
public void toggleWhenPressed(final Command command) {
|
||||
toggleWhenActive(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the command when the button is pressed.
|
||||
*
|
||||
* @param command the command to start
|
||||
*/
|
||||
public void cancelWhenPressed(final Command command) {
|
||||
cancelWhenActive(command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.buttons;
|
||||
|
||||
/**
|
||||
* This class is intended to be used within a program. The programmer can manually set its value.
|
||||
* Also includes a setting for whether or not it should invert its value.
|
||||
*/
|
||||
public class InternalButton extends Button {
|
||||
|
||||
private boolean m_pressed;
|
||||
private boolean m_inverted;
|
||||
|
||||
/**
|
||||
* Creates an InternalButton that is not inverted.
|
||||
*/
|
||||
public InternalButton() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an InternalButton which is inverted depending on the input.
|
||||
*
|
||||
* @param inverted if false, then this button is pressed when set to true, otherwise it is pressed
|
||||
* when set to false.
|
||||
*/
|
||||
public InternalButton(boolean inverted) {
|
||||
m_pressed = m_inverted = inverted;
|
||||
}
|
||||
|
||||
public void setInverted(boolean inverted) {
|
||||
m_inverted = inverted;
|
||||
}
|
||||
|
||||
public void setPressed(boolean pressed) {
|
||||
m_pressed = pressed;
|
||||
}
|
||||
|
||||
public boolean get() {
|
||||
return m_pressed ^ m_inverted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.buttons;
|
||||
|
||||
import edu.wpi.first.wpilibj.GenericHID;
|
||||
|
||||
/**
|
||||
* A {@link Button} that gets its state from a {@link GenericHID}.
|
||||
*/
|
||||
public class JoystickButton extends Button {
|
||||
|
||||
private final GenericHID m_joystick;
|
||||
private final int m_buttonNumber;
|
||||
|
||||
/**
|
||||
* Create a joystick button for triggering commands.
|
||||
*
|
||||
* @param joystick The GenericHID object that has the button (e.g. Joystick, KinectStick,
|
||||
* etc)
|
||||
* @param buttonNumber The button number (see {@link GenericHID#getRawButton(int) }
|
||||
*/
|
||||
public JoystickButton(GenericHID joystick, int buttonNumber) {
|
||||
m_joystick = joystick;
|
||||
m_buttonNumber = buttonNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the joystick button.
|
||||
*
|
||||
* @return The value of the joystick button
|
||||
*/
|
||||
public boolean get() {
|
||||
return m_joystick.getRawButton(m_buttonNumber);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.buttons;
|
||||
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
|
||||
/**
|
||||
* A {@link Button} that uses a {@link NetworkTable} boolean field.
|
||||
*/
|
||||
public class NetworkButton extends Button {
|
||||
|
||||
private final NetworkTable m_table;
|
||||
private final String m_field;
|
||||
|
||||
public NetworkButton(String table, String field) {
|
||||
this(NetworkTable.getTable(table), field);
|
||||
}
|
||||
|
||||
public NetworkButton(NetworkTable table, String field) {
|
||||
m_table = table;
|
||||
m_field = field;
|
||||
}
|
||||
|
||||
public boolean get() {
|
||||
return m_table.isConnected() && m_table.getBoolean(m_field, false);
|
||||
}
|
||||
}
|
||||
211
wpilibj/src/main/java/edu/wpi/first/wpilibj/buttons/Trigger.java
Normal file
211
wpilibj/src/main/java/edu/wpi/first/wpilibj/buttons/Trigger.java
Normal file
@@ -0,0 +1,211 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.buttons;
|
||||
|
||||
import edu.wpi.first.wpilibj.Sendable;
|
||||
import edu.wpi.first.wpilibj.command.Command;
|
||||
import edu.wpi.first.wpilibj.command.Scheduler;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* This class provides an easy way to link commands to inputs.
|
||||
*
|
||||
* <p>It is very easy to link a button to a command. For instance, you could link the trigger
|
||||
* button of a joystick to a "score" command.
|
||||
*
|
||||
* <p>It is encouraged that teams write a subclass of Trigger if they want to have something unusual
|
||||
* (for instance, if they want to react to the user holding a button while the robot is reading a
|
||||
* certain sensor input). For this, they only have to write the {@link Trigger#get()} method to get
|
||||
* the full functionality of the Trigger class.
|
||||
*/
|
||||
public abstract class Trigger implements Sendable {
|
||||
|
||||
/**
|
||||
* Returns whether or not the trigger is active.
|
||||
*
|
||||
* <p>This method will be called repeatedly a command is linked to the Trigger.
|
||||
*
|
||||
* @return whether or not the trigger condition is active.
|
||||
*/
|
||||
public abstract boolean get();
|
||||
|
||||
/**
|
||||
* Returns whether get() return true or the internal table for SmartDashboard use is pressed.
|
||||
*
|
||||
* @return whether get() return true or the internal table for SmartDashboard use is pressed.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UselessParentheses")
|
||||
private boolean grab() {
|
||||
return get() || (m_table != null && m_table.getBoolean("pressed", false));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the given command whenever the trigger just becomes active.
|
||||
*
|
||||
* @param command the command to start
|
||||
*/
|
||||
public void whenActive(final Command command) {
|
||||
new ButtonScheduler() {
|
||||
|
||||
private boolean m_pressedLast = grab();
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (grab()) {
|
||||
if (!m_pressedLast) {
|
||||
m_pressedLast = true;
|
||||
command.start();
|
||||
}
|
||||
} else {
|
||||
m_pressedLast = false;
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constantly starts the given command while the button is held.
|
||||
*
|
||||
* {@link Command#start()} will be called repeatedly while the trigger is active, and will be
|
||||
* canceled when the trigger becomes inactive.
|
||||
*
|
||||
* @param command the command to start
|
||||
*/
|
||||
public void whileActive(final Command command) {
|
||||
new ButtonScheduler() {
|
||||
|
||||
private boolean m_pressedLast = grab();
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (grab()) {
|
||||
m_pressedLast = true;
|
||||
command.start();
|
||||
} else {
|
||||
if (m_pressedLast) {
|
||||
m_pressedLast = false;
|
||||
command.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the command when the trigger becomes inactive.
|
||||
*
|
||||
* @param command the command to start
|
||||
*/
|
||||
public void whenInactive(final Command command) {
|
||||
new ButtonScheduler() {
|
||||
|
||||
private boolean m_pressedLast = grab();
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (grab()) {
|
||||
m_pressedLast = true;
|
||||
} else {
|
||||
if (m_pressedLast) {
|
||||
m_pressedLast = false;
|
||||
command.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a command when the trigger becomes active.
|
||||
*
|
||||
* @param command the command to toggle
|
||||
*/
|
||||
public void toggleWhenActive(final Command command) {
|
||||
new ButtonScheduler() {
|
||||
|
||||
private boolean m_pressedLast = grab();
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (grab()) {
|
||||
if (!m_pressedLast) {
|
||||
m_pressedLast = true;
|
||||
if (command.isRunning()) {
|
||||
command.cancel();
|
||||
} else {
|
||||
command.start();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_pressedLast = false;
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a command when the trigger becomes active.
|
||||
*
|
||||
* @param command the command to cancel
|
||||
*/
|
||||
public void cancelWhenActive(final Command command) {
|
||||
new ButtonScheduler() {
|
||||
|
||||
private boolean m_pressedLast = grab();
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (grab()) {
|
||||
if (!m_pressedLast) {
|
||||
m_pressedLast = true;
|
||||
command.cancel();
|
||||
}
|
||||
} else {
|
||||
m_pressedLast = false;
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal class of {@link Trigger}. The user should ignore this, it is only public to
|
||||
* interface between packages.
|
||||
*/
|
||||
public abstract class ButtonScheduler {
|
||||
public abstract void execute();
|
||||
|
||||
protected void start() {
|
||||
Scheduler.getInstance().addButton(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These methods continue to return the "Button" SmartDashboard type until we decided to create a
|
||||
* Trigger widget type for the dashboard.
|
||||
*/
|
||||
@Override
|
||||
public String getSmartDashboardType() {
|
||||
return "Button";
|
||||
}
|
||||
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable table) {
|
||||
m_table = table;
|
||||
if (table != null) {
|
||||
table.putBoolean("pressed", get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.can;
|
||||
|
||||
import edu.wpi.first.wpilibj.communication.NIRioStatus;
|
||||
import edu.wpi.first.wpilibj.util.UncleanStatusException;
|
||||
|
||||
public class CANExceptionFactory {
|
||||
// FRC Error codes
|
||||
static final int ERR_CANSessionMux_InvalidBuffer = -44086;
|
||||
static final int ERR_CANSessionMux_MessageNotFound = -44087;
|
||||
static final int ERR_CANSessionMux_NotAllowed = -44088;
|
||||
static final int ERR_CANSessionMux_NotInitialized = -44089;
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
public static void checkStatus(int status, int messageID) throws CANInvalidBufferException,
|
||||
CANMessageNotAllowedException, CANNotInitializedException, UncleanStatusException {
|
||||
switch (status) {
|
||||
case NIRioStatus.kRioStatusSuccess:
|
||||
// Everything is ok... don't throw.
|
||||
return;
|
||||
case ERR_CANSessionMux_InvalidBuffer:
|
||||
case NIRioStatus.kRIOStatusBufferInvalidSize:
|
||||
throw new CANInvalidBufferException();
|
||||
case ERR_CANSessionMux_MessageNotFound:
|
||||
case NIRioStatus.kRIOStatusOperationTimedOut:
|
||||
throw new CANMessageNotFoundException();
|
||||
case ERR_CANSessionMux_NotAllowed:
|
||||
case NIRioStatus.kRIOStatusFeatureNotSupported:
|
||||
throw new CANMessageNotAllowedException("MessageID = " + Integer.toString(messageID));
|
||||
case ERR_CANSessionMux_NotInitialized:
|
||||
case NIRioStatus.kRIOStatusResourceNotInitialized:
|
||||
throw new CANNotInitializedException();
|
||||
default:
|
||||
throw new UncleanStatusException("Fatal status code detected: " + Integer.toString(
|
||||
status));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.can;
|
||||
|
||||
/**
|
||||
* Exception indicating that a CAN driver library entry-point was passed an invalid buffer.
|
||||
* Typically, this is due to a buffer being too small to include the needed safety token.
|
||||
*/
|
||||
public class CANInvalidBufferException extends RuntimeException {
|
||||
public CANInvalidBufferException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CANInvalidBufferException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
32
wpilibj/src/main/java/edu/wpi/first/wpilibj/can/CANJNI.java
Normal file
32
wpilibj/src/main/java/edu/wpi/first/wpilibj/can/CANJNI.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.can;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import edu.wpi.first.wpilibj.hal.JNIWrapper;
|
||||
|
||||
@SuppressWarnings("AbbreviationAsWordInName")
|
||||
public class CANJNI extends JNIWrapper {
|
||||
public static final int CAN_SEND_PERIOD_NO_REPEAT = 0;
|
||||
public static final int CAN_SEND_PERIOD_STOP_REPEATING = -1;
|
||||
|
||||
/* Flags in the upper bits of the messageID */
|
||||
public static final int CAN_IS_FRAME_REMOTE = 0x80000000;
|
||||
public static final int CAN_IS_FRAME_11BIT = 0x40000000;
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
public static native void FRCNetCommCANSessionMuxSendMessage(int messageID,
|
||||
byte[] data,
|
||||
int periodMs);
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
public static native byte[] FRCNetCommCANSessionMuxReceiveMessage(
|
||||
IntBuffer messageID, int messageIDMask, ByteBuffer timeStamp);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.can;
|
||||
|
||||
/**
|
||||
* Exception indicating that the Jaguar CAN Driver layer refused to send a restricted message ID to
|
||||
* the CAN bus.
|
||||
*/
|
||||
public class CANMessageNotAllowedException extends RuntimeException {
|
||||
public CANMessageNotAllowedException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.can;
|
||||
|
||||
/**
|
||||
* Exception indicating that a can message is not available from Network Communications. This
|
||||
* usually just means we already have the most recent value cached locally.
|
||||
*/
|
||||
public class CANMessageNotFoundException extends RuntimeException {
|
||||
public CANMessageNotFoundException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CANMessageNotFoundException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.can;
|
||||
|
||||
/**
|
||||
* Exception indicating that the CAN driver layer has not been initialized. This happens when an
|
||||
* entry-point is called before a CAN driver plugin has been installed.
|
||||
*/
|
||||
public class CANNotInitializedException extends RuntimeException {
|
||||
public CANNotInitializedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CANNotInitializedException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
594
wpilibj/src/main/java/edu/wpi/first/wpilibj/command/Command.java
Normal file
594
wpilibj/src/main/java/edu/wpi/first/wpilibj/command/Command.java
Normal file
@@ -0,0 +1,594 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.command;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import edu.wpi.first.wpilibj.NamedSendable;
|
||||
import edu.wpi.first.wpilibj.RobotState;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
|
||||
/**
|
||||
* The Command class is at the very core of the entire command framework. Every command can be
|
||||
* started with a call to {@link Command#start() start()}. Once a command is started it will call
|
||||
* {@link Command#initialize() initialize()}, and then will repeatedly call {@link Command#execute()
|
||||
* execute()} until the {@link Command#isFinished() isFinished()} returns true. Once it does, {@link
|
||||
* Command#end() end()} will be called.
|
||||
*
|
||||
* <p>However, if at any point while it is running {@link Command#cancel() cancel()} is called,
|
||||
* then the command will be stopped and {@link Command#interrupted() interrupted()} will be called.
|
||||
*
|
||||
* <p>If a command uses a {@link Subsystem}, then it should specify that it does so by calling the
|
||||
* {@link Command#requires(Subsystem) requires(...)} method in its constructor. Note that a Command
|
||||
* may have multiple requirements, and {@link Command#requires(Subsystem) requires(...)} should be
|
||||
* called for each one.
|
||||
*
|
||||
* <p>If a command is running and a new command with shared requirements is started, then one of
|
||||
* two things will happen. If the active command is interruptible, then {@link Command#cancel()
|
||||
* cancel()} will be called and the command will be removed to make way for the new one. If the
|
||||
* active command is not interruptible, the other one will not even be started, and the active one
|
||||
* will continue functioning.
|
||||
*
|
||||
* @see Subsystem
|
||||
* @see CommandGroup
|
||||
* @see IllegalUseOfCommandException
|
||||
*/
|
||||
public abstract class Command implements NamedSendable {
|
||||
|
||||
/**
|
||||
* The name of this command.
|
||||
*/
|
||||
private String m_name;
|
||||
/**
|
||||
* The time since this command was initialized.
|
||||
*/
|
||||
private double m_startTime = -1;
|
||||
/**
|
||||
* The time (in seconds) before this command "times out" (or -1 if no timeout).
|
||||
*/
|
||||
private double m_timeout = -1;
|
||||
/**
|
||||
* Whether or not this command has been initialized.
|
||||
*/
|
||||
private boolean m_initialized = false;
|
||||
/**
|
||||
* The requirements (or null if no requirements).
|
||||
*/
|
||||
private Set m_requirements;
|
||||
/**
|
||||
* Whether or not it is running.
|
||||
*/
|
||||
private boolean m_running = false;
|
||||
/**
|
||||
* Whether or not it is interruptible.
|
||||
*/
|
||||
private boolean m_interruptible = true;
|
||||
/**
|
||||
* Whether or not it has been canceled.
|
||||
*/
|
||||
private boolean m_canceled = false;
|
||||
/**
|
||||
* Whether or not it has been locked.
|
||||
*/
|
||||
private boolean m_locked = false;
|
||||
/**
|
||||
* Whether this command should run when the robot is disabled.
|
||||
*/
|
||||
private boolean m_runWhenDisabled = false;
|
||||
/**
|
||||
* The {@link CommandGroup} this is in.
|
||||
*/
|
||||
private CommandGroup m_parent;
|
||||
|
||||
/**
|
||||
* Creates a new command. The name of this command will be set to its class name.
|
||||
*/
|
||||
public Command() {
|
||||
m_name = getClass().getName();
|
||||
m_name = m_name.substring(m_name.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new command with the given name.
|
||||
*
|
||||
* @param name the name for this command
|
||||
* @throws IllegalArgumentException if name is null
|
||||
*/
|
||||
public Command(String name) {
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("Name must not be null.");
|
||||
}
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new command with the given timeout and a default name. The default name is the name
|
||||
* of the class.
|
||||
*
|
||||
* @param timeout the time (in seconds) before this command "times out"
|
||||
* @throws IllegalArgumentException if given a negative timeout
|
||||
* @see Command#isTimedOut() isTimedOut()
|
||||
*/
|
||||
public Command(double timeout) {
|
||||
this();
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("Timeout must not be negative. Given:" + timeout);
|
||||
}
|
||||
m_timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new command with the given name and timeout.
|
||||
*
|
||||
* @param name the name of the command
|
||||
* @param timeout the time (in seconds) before this command "times out"
|
||||
* @throws IllegalArgumentException if given a negative timeout or name was null.
|
||||
* @see Command#isTimedOut() isTimedOut()
|
||||
*/
|
||||
public Command(String name, double timeout) {
|
||||
this(name);
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("Timeout must not be negative. Given:" + timeout);
|
||||
}
|
||||
m_timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this command. If no name was specified in the constructor, then the default
|
||||
* is the name of the class.
|
||||
*
|
||||
* @return the name of this command
|
||||
*/
|
||||
public String getName() {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timeout of this command.
|
||||
*
|
||||
* @param seconds the timeout (in seconds)
|
||||
* @throws IllegalArgumentException if seconds is negative
|
||||
* @see Command#isTimedOut() isTimedOut()
|
||||
*/
|
||||
protected final synchronized void setTimeout(double seconds) {
|
||||
if (seconds < 0) {
|
||||
throw new IllegalArgumentException("Seconds must be positive. Given:" + seconds);
|
||||
}
|
||||
m_timeout = seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time since this command was initialized (in seconds). This function will work even
|
||||
* if there is no specified timeout.
|
||||
*
|
||||
* @return the time since this command was initialized (in seconds).
|
||||
*/
|
||||
public final synchronized double timeSinceInitialized() {
|
||||
return m_startTime < 0 ? 0 : Timer.getFPGATimestamp() - m_startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method specifies that the given {@link Subsystem} is used by this command. This method is
|
||||
* crucial to the functioning of the Command System in general.
|
||||
*
|
||||
* <p>Note that the recommended way to call this method is in the constructor.
|
||||
*
|
||||
* @param subsystem the {@link Subsystem} required
|
||||
* @throws IllegalArgumentException if subsystem is null
|
||||
* @throws IllegalUseOfCommandException if this command has started before or if it has been given
|
||||
* to a {@link CommandGroup}
|
||||
* @see Subsystem
|
||||
*/
|
||||
protected synchronized void requires(Subsystem subsystem) {
|
||||
validate("Can not add new requirement to command");
|
||||
if (subsystem != null) {
|
||||
if (m_requirements == null) {
|
||||
m_requirements = new Set();
|
||||
}
|
||||
m_requirements.add(subsystem);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Subsystem must not be null.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the command has been removed. This will call {@link Command#interrupted()
|
||||
* interrupted()} or {@link Command#end() end()}.
|
||||
*/
|
||||
synchronized void removed() {
|
||||
if (m_initialized) {
|
||||
if (isCanceled()) {
|
||||
interrupted();
|
||||
_interrupted();
|
||||
} else {
|
||||
end();
|
||||
_end();
|
||||
}
|
||||
}
|
||||
m_initialized = false;
|
||||
m_canceled = false;
|
||||
m_running = false;
|
||||
if (m_table != null) {
|
||||
m_table.putBoolean("running", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The run method is used internally to actually run the commands.
|
||||
*
|
||||
* @return whether or not the command should stay within the {@link Scheduler}.
|
||||
*/
|
||||
synchronized boolean run() {
|
||||
if (!m_runWhenDisabled && m_parent == null && RobotState.isDisabled()) {
|
||||
cancel();
|
||||
}
|
||||
if (isCanceled()) {
|
||||
return false;
|
||||
}
|
||||
if (!m_initialized) {
|
||||
m_initialized = true;
|
||||
startTiming();
|
||||
_initialize();
|
||||
initialize();
|
||||
}
|
||||
_execute();
|
||||
execute();
|
||||
return !isFinished();
|
||||
}
|
||||
|
||||
/**
|
||||
* The initialize method is called the first time this Command is run after being started.
|
||||
*/
|
||||
protected void initialize() {}
|
||||
|
||||
/**
|
||||
* A shadow method called before {@link Command#initialize() initialize()}.
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
void _initialize() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The execute method is called repeatedly until this Command either finishes or is canceled.
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
protected void execute() {}
|
||||
|
||||
/**
|
||||
* A shadow method called before {@link Command#execute() execute()}.
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
void _execute() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this command is finished. If it is, then the command will be removed and {@link
|
||||
* Command#end() end()} will be called.
|
||||
*
|
||||
* <p>It may be useful for a team to reference the {@link Command#isTimedOut() isTimedOut()}
|
||||
* method for time-sensitive commands.
|
||||
*
|
||||
* <p>Returning false will result in the command never ending automatically. It may still be
|
||||
* cancelled manually or interrupted by another command. Returning true will result in the
|
||||
* command executing once and finishing immediately. We recommend using {@link InstantCommand}
|
||||
* for this.
|
||||
*
|
||||
* @return whether this command is finished.
|
||||
* @see Command#isTimedOut() isTimedOut()
|
||||
*/
|
||||
protected abstract boolean isFinished();
|
||||
|
||||
/**
|
||||
* Called when the command ended peacefully. This is where you may want to wrap up loose ends,
|
||||
* like shutting off a motor that was being used in the command.
|
||||
*/
|
||||
protected void end() {}
|
||||
|
||||
/**
|
||||
* A shadow method called after {@link Command#end() end()}.
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
void _end() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the command ends because somebody called {@link Command#cancel() cancel()} or
|
||||
* another command shared the same requirements as this one, and booted it out.
|
||||
*
|
||||
* <p>This is where you may want to wrap up loose ends, like shutting off a motor that was being
|
||||
* used in the command.
|
||||
*
|
||||
* <p>Generally, it is useful to simply call the {@link Command#end() end()} method within this
|
||||
* method, as done here.
|
||||
*/
|
||||
protected void interrupted() {
|
||||
end();
|
||||
}
|
||||
|
||||
/**
|
||||
* A shadow method called after {@link Command#interrupted() interrupted()}.
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
void _interrupted() {}
|
||||
|
||||
/**
|
||||
* Called to indicate that the timer should start. This is called right before {@link
|
||||
* Command#initialize() initialize()} is, inside the {@link Command#run() run()} method.
|
||||
*/
|
||||
private void startTiming() {
|
||||
m_startTime = Timer.getFPGATimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the {@link Command#timeSinceInitialized() timeSinceInitialized()} method
|
||||
* returns a number which is greater than or equal to the timeout for the command. If there is no
|
||||
* timeout, this will always return false.
|
||||
*
|
||||
* @return whether the time has expired
|
||||
*/
|
||||
protected synchronized boolean isTimedOut() {
|
||||
return m_timeout != -1 && timeSinceInitialized() >= m_timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requirements (as an {@link Enumeration Enumeration} of {@link Subsystem
|
||||
* Subsystems}) of this command.
|
||||
*
|
||||
* @return the requirements (as an {@link Enumeration Enumeration} of {@link Subsystem
|
||||
* Subsystems}) of this command
|
||||
*/
|
||||
synchronized Enumeration getRequirements() {
|
||||
return m_requirements == null ? emptyEnumeration : m_requirements.getElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents further changes from being made.
|
||||
*/
|
||||
synchronized void lockChanges() {
|
||||
m_locked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If changes are locked, then this will throw an {@link IllegalUseOfCommandException}.
|
||||
*
|
||||
* @param message the message to say (it is appended by a default message)
|
||||
*/
|
||||
synchronized void validate(String message) {
|
||||
if (m_locked) {
|
||||
throw new IllegalUseOfCommandException(message
|
||||
+ " after being started or being added to a command group");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent of this command. No actual change is made to the group.
|
||||
*
|
||||
* @param parent the parent
|
||||
* @throws IllegalUseOfCommandException if this {@link Command} already is already in a group
|
||||
*/
|
||||
synchronized void setParent(CommandGroup parent) {
|
||||
if (m_parent != null) {
|
||||
throw new IllegalUseOfCommandException(
|
||||
"Can not give command to a command group after already being put in a command group");
|
||||
}
|
||||
lockChanges();
|
||||
m_parent = parent;
|
||||
if (m_table != null) {
|
||||
m_table.putBoolean("isParented", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears list of subsystem requirements. This is only used by
|
||||
* {@link ConditionalCommand} so cancelling the chosen command works properly
|
||||
* in {@link CommandGroup}.
|
||||
*/
|
||||
protected void clearRequirements() {
|
||||
m_requirements = new Set();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts up the command. Gets the command ready to start. <p> Note that the command will
|
||||
* eventually start, however it will not necessarily do so immediately, and may in fact be
|
||||
* canceled before initialize is even called. </p>
|
||||
*
|
||||
* @throws IllegalUseOfCommandException if the command is a part of a CommandGroup
|
||||
*/
|
||||
public synchronized void start() {
|
||||
lockChanges();
|
||||
if (m_parent != null) {
|
||||
throw new IllegalUseOfCommandException(
|
||||
"Can not start a command that is a part of a command group");
|
||||
}
|
||||
Scheduler.getInstance().add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used internally to mark that the command has been started. The lifecycle of a command
|
||||
* is:
|
||||
*
|
||||
* <p>startRunning() is called. run() is called (multiple times potentially) removed() is called.
|
||||
*
|
||||
* <p>It is very important that startRunning and removed be called in order or some assumptions of
|
||||
* the code will be broken.
|
||||
*/
|
||||
synchronized void startRunning() {
|
||||
m_running = true;
|
||||
m_startTime = -1;
|
||||
if (m_table != null) {
|
||||
m_table.putBoolean("running", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the command is running. This may return true even if the command has
|
||||
* just been canceled, as it may not have yet called {@link Command#interrupted()}.
|
||||
*
|
||||
* @return whether or not the command is running
|
||||
*/
|
||||
public synchronized boolean isRunning() {
|
||||
return m_running;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will cancel the current command. <p> This will cancel the current command eventually. It
|
||||
* can be called multiple times. And it can be called when the command is not running. If the
|
||||
* command is running though, then the command will be marked as canceled and eventually removed.
|
||||
* </p> <p> A command can not be canceled if it is a part of a command group, you must cancel the
|
||||
* command group instead. </p>
|
||||
*
|
||||
* @throws IllegalUseOfCommandException if this command is a part of a command group
|
||||
*/
|
||||
public synchronized void cancel() {
|
||||
if (m_parent != null) {
|
||||
throw new IllegalUseOfCommandException("Can not manually cancel a command in a command "
|
||||
+ "group");
|
||||
}
|
||||
_cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* This works like cancel(), except that it doesn't throw an exception if it is a part of a
|
||||
* command group. Should only be called by the parent command group.
|
||||
*/
|
||||
@SuppressWarnings("MethodName")
|
||||
synchronized void _cancel() {
|
||||
if (isRunning()) {
|
||||
m_canceled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this has been canceled.
|
||||
*
|
||||
* @return whether or not this has been canceled
|
||||
*/
|
||||
public synchronized boolean isCanceled() {
|
||||
return m_canceled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this command can be interrupted.
|
||||
*
|
||||
* @return whether or not this command can be interrupted
|
||||
*/
|
||||
public synchronized boolean isInterruptible() {
|
||||
return m_interruptible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not this command can be interrupted.
|
||||
*
|
||||
* @param interruptible whether or not this command can be interrupted
|
||||
*/
|
||||
protected synchronized void setInterruptible(boolean interruptible) {
|
||||
m_interruptible = interruptible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the command requires the given {@link Subsystem}.
|
||||
*
|
||||
* @param system the system
|
||||
* @return whether or not the subsystem is required, or false if given null
|
||||
*/
|
||||
public synchronized boolean doesRequire(Subsystem system) {
|
||||
return m_requirements != null && m_requirements.contains(system);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link CommandGroup} that this command is a part of. Will return null if this
|
||||
* {@link Command} is not in a group.
|
||||
*
|
||||
* @return the {@link CommandGroup} that this command is a part of (or null if not in group)
|
||||
*/
|
||||
public synchronized CommandGroup getGroup() {
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not this {@link Command} should run when the robot is disabled.
|
||||
*
|
||||
* <p>By default a command will not run when the robot is disabled, and will in fact be canceled.
|
||||
*
|
||||
* @param run whether or not this command should run when the robot is disabled
|
||||
*/
|
||||
public void setRunWhenDisabled(boolean run) {
|
||||
m_runWhenDisabled = run;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this {@link Command} will run when the robot is disabled, or if it will
|
||||
* cancel itself.
|
||||
*
|
||||
* @return True if this command will run when the robot is disabled.
|
||||
*/
|
||||
public boolean willRunWhenDisabled() {
|
||||
return m_runWhenDisabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* An empty enumeration given whenever there are no requirements.
|
||||
*/
|
||||
private static Enumeration emptyEnumeration = new Enumeration() {
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object nextElement() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The string representation for a {@link Command} is by default its name.
|
||||
*
|
||||
* @return the string representation of this object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
|
||||
public String getSmartDashboardType() {
|
||||
return "Command";
|
||||
}
|
||||
|
||||
private final ITableListener m_listener = (table1, key, value, isNew) -> {
|
||||
if (((Boolean) value).booleanValue()) {
|
||||
start();
|
||||
} else {
|
||||
cancel();
|
||||
}
|
||||
};
|
||||
private ITable m_table;
|
||||
|
||||
@Override
|
||||
public void initTable(ITable table) {
|
||||
if (m_table != null) {
|
||||
m_table.removeTableListener(m_listener);
|
||||
}
|
||||
m_table = table;
|
||||
if (table != null) {
|
||||
table.putString("name", getName());
|
||||
table.putBoolean("running", isRunning());
|
||||
table.putBoolean("isParented", m_parent != null);
|
||||
table.addTableListener("running", m_listener, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITable getTable() {
|
||||
return m_table;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.command;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Vector;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* A {@link CommandGroup} is a list of commands which are executed in sequence.
|
||||
*
|
||||
* <p> Commands in a {@link CommandGroup} are added using the {@link
|
||||
* CommandGroup#addSequential(Command) addSequential(...)} method and are called sequentially.
|
||||
* {@link CommandGroup CommandGroups} are themselves {@link Command commands} and can be given to
|
||||
* other {@link CommandGroup CommandGroups}. </p>
|
||||
*
|
||||
* <p> {@link CommandGroup CommandGroups} will carry all of the requirements of their {@link Command
|
||||
* subcommands}. Additional requirements can be specified by calling {@link
|
||||
* CommandGroup#requires(Subsystem) requires(...)} normally in the constructor. </P>
|
||||
*
|
||||
* <p> CommandGroups can also execute commands in parallel, simply by adding them using {@link
|
||||
* CommandGroup#addParallel(Command) addParallel(...)}. </p>
|
||||
*
|
||||
* @see Command
|
||||
* @see Subsystem
|
||||
* @see IllegalUseOfCommandException
|
||||
*/
|
||||
public class CommandGroup extends Command {
|
||||
|
||||
/**
|
||||
* The commands in this group (stored in entries).
|
||||
*/
|
||||
private final Vector<Entry> m_commands = new Vector<>();
|
||||
/*
|
||||
* Intentionally package private
|
||||
*/
|
||||
/**
|
||||
* The active children in this group (stored in entries).
|
||||
*/
|
||||
final Vector<Entry> m_children = new Vector<>();
|
||||
/**
|
||||
* The current command, -1 signifies that none have been run.
|
||||
*/
|
||||
private int m_currentCommandIndex = -1;
|
||||
|
||||
/**
|
||||
* Creates a new {@link CommandGroup CommandGroup}. The name of this command will be set to its
|
||||
* class name.
|
||||
*/
|
||||
public CommandGroup() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link CommandGroup CommandGroup} with the given name.
|
||||
*
|
||||
* @param name the name for this command group
|
||||
* @throws IllegalArgumentException if name is null
|
||||
*/
|
||||
public CommandGroup(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new {@link Command Command} to the group. The {@link Command Command} will be started
|
||||
* after all the previously added {@link Command Commands}.
|
||||
*
|
||||
* <p> Note that any requirements the given {@link Command Command} has will be added to the
|
||||
* group. For this reason, a {@link Command Command's} requirements can not be changed after being
|
||||
* added to a group. </p>
|
||||
*
|
||||
* <p> It is recommended that this method be called in the constructor. </p>
|
||||
*
|
||||
* @param command The {@link Command Command} to be added
|
||||
* @throws IllegalUseOfCommandException if the group has been started before or been given to
|
||||
* another group
|
||||
* @throws IllegalArgumentException if command is null
|
||||
*/
|
||||
public final synchronized void addSequential(Command command) {
|
||||
validate("Can not add new command to command group");
|
||||
if (command == null) {
|
||||
throw new IllegalArgumentException("Given null command");
|
||||
}
|
||||
|
||||
command.setParent(this);
|
||||
|
||||
m_commands.addElement(new Entry(command, Entry.IN_SEQUENCE));
|
||||
for (Enumeration e = command.getRequirements(); e.hasMoreElements(); ) {
|
||||
requires((Subsystem) e.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new {@link Command Command} to the group with a given timeout. The {@link Command
|
||||
* Command} will be started after all the previously added commands.
|
||||
*
|
||||
* <p> Once the {@link Command Command} is started, it will be run until it finishes or the time
|
||||
* expires, whichever is sooner. Note that the given {@link Command Command} will have no
|
||||
* knowledge that it is on a timer. </p>
|
||||
*
|
||||
* <p> Note that any requirements the given {@link Command Command} has will be added to the
|
||||
* group. For this reason, a {@link Command Command's} requirements can not be changed after being
|
||||
* added to a group. </p>
|
||||
*
|
||||
* <p> It is recommended that this method be called in the constructor. </p>
|
||||
*
|
||||
* @param command The {@link Command Command} to be added
|
||||
* @param timeout The timeout (in seconds)
|
||||
* @throws IllegalUseOfCommandException if the group has been started before or been given to
|
||||
* another group or if the {@link Command Command} has been
|
||||
* started before or been given to another group
|
||||
* @throws IllegalArgumentException if command is null or timeout is negative
|
||||
*/
|
||||
public final synchronized void addSequential(Command command, double timeout) {
|
||||
validate("Can not add new command to command group");
|
||||
if (command == null) {
|
||||
throw new IllegalArgumentException("Given null command");
|
||||
}
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("Can not be given a negative timeout");
|
||||
}
|
||||
|
||||
command.setParent(this);
|
||||
|
||||
m_commands.addElement(new Entry(command, Entry.IN_SEQUENCE, timeout));
|
||||
for (Enumeration e = command.getRequirements(); e.hasMoreElements(); ) {
|
||||
requires((Subsystem) e.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new child {@link Command} to the group. The {@link Command} will be started after all
|
||||
* the previously added {@link Command Commands}.
|
||||
*
|
||||
* <p> Instead of waiting for the child to finish, a {@link CommandGroup} will have it run at the
|
||||
* same time as the subsequent {@link Command Commands}. The child will run until either it
|
||||
* finishes, a new child with conflicting requirements is started, or the main sequence runs a
|
||||
* {@link Command} with conflicting requirements. In the latter two cases, the child will be
|
||||
* canceled even if it says it can't be interrupted. </p>
|
||||
*
|
||||
* <p> Note that any requirements the given {@link Command Command} has will be added to the
|
||||
* group. For this reason, a {@link Command Command's} requirements can not be changed after being
|
||||
* added to a group. </p>
|
||||
*
|
||||
* <p> It is recommended that this method be called in the constructor. </p>
|
||||
*
|
||||
* @param command The command to be added
|
||||
* @throws IllegalUseOfCommandException if the group has been started before or been given to
|
||||
* another command group
|
||||
* @throws IllegalArgumentException if command is null
|
||||
*/
|
||||
public final synchronized void addParallel(Command command) {
|
||||
requireNonNull(command, "Provided command was null");
|
||||
validate("Can not add new command to command group");
|
||||
|
||||
command.setParent(this);
|
||||
|
||||
m_commands.addElement(new Entry(command, Entry.BRANCH_CHILD));
|
||||
for (Enumeration e = command.getRequirements(); e.hasMoreElements(); ) {
|
||||
requires((Subsystem) e.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new child {@link Command} to the group with the given timeout. The {@link Command} will
|
||||
* be started after all the previously added {@link Command Commands}.
|
||||
*
|
||||
* <p> Once the {@link Command Command} is started, it will run until it finishes, is interrupted,
|
||||
* or the time expires, whichever is sooner. Note that the given {@link Command Command} will have
|
||||
* no knowledge that it is on a timer. </p>
|
||||
*
|
||||
* <p> Instead of waiting for the child to finish, a {@link CommandGroup} will have it run at the
|
||||
* same time as the subsequent {@link Command Commands}. The child will run until either it
|
||||
* finishes, the timeout expires, a new child with conflicting requirements is started, or the
|
||||
* main sequence runs a {@link Command} with conflicting requirements. In the latter two cases,
|
||||
* the child will be canceled even if it says it can't be interrupted. </p>
|
||||
*
|
||||
* <p> Note that any requirements the given {@link Command Command} has will be added to the
|
||||
* group. For this reason, a {@link Command Command's} requirements can not be changed after being
|
||||
* added to a group. </p>
|
||||
*
|
||||
* <p> It is recommended that this method be called in the constructor. </p>
|
||||
*
|
||||
* @param command The command to be added
|
||||
* @param timeout The timeout (in seconds)
|
||||
* @throws IllegalUseOfCommandException if the group has been started before or been given to
|
||||
* another command group
|
||||
* @throws IllegalArgumentException if command is null
|
||||
*/
|
||||
public final synchronized void addParallel(Command command, double timeout) {
|
||||
requireNonNull(command, "Provided command was null");
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("Can not be given a negative timeout");
|
||||
}
|
||||
validate("Can not add new command to command group");
|
||||
|
||||
command.setParent(this);
|
||||
|
||||
m_commands.addElement(new Entry(command, Entry.BRANCH_CHILD, timeout));
|
||||
for (Enumeration e = command.getRequirements(); e.hasMoreElements(); ) {
|
||||
requires((Subsystem) e.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
void _initialize() {
|
||||
m_currentCommandIndex = -1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
void _execute() {
|
||||
Entry entry = null;
|
||||
Command cmd = null;
|
||||
boolean firstRun = false;
|
||||
if (m_currentCommandIndex == -1) {
|
||||
firstRun = true;
|
||||
m_currentCommandIndex = 0;
|
||||
}
|
||||
|
||||
while (m_currentCommandIndex < m_commands.size()) {
|
||||
|
||||
if (cmd != null) {
|
||||
if (entry.isTimedOut()) {
|
||||
cmd._cancel();
|
||||
}
|
||||
if (cmd.run()) {
|
||||
break;
|
||||
} else {
|
||||
cmd.removed();
|
||||
m_currentCommandIndex++;
|
||||
firstRun = true;
|
||||
cmd = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
entry = m_commands.elementAt(m_currentCommandIndex);
|
||||
cmd = null;
|
||||
|
||||
switch (entry.m_state) {
|
||||
case Entry.IN_SEQUENCE:
|
||||
cmd = entry.m_command;
|
||||
if (firstRun) {
|
||||
cmd.startRunning();
|
||||
cancelConflicts(cmd);
|
||||
}
|
||||
firstRun = false;
|
||||
break;
|
||||
case Entry.BRANCH_PEER:
|
||||
m_currentCommandIndex++;
|
||||
entry.m_command.start();
|
||||
break;
|
||||
case Entry.BRANCH_CHILD:
|
||||
m_currentCommandIndex++;
|
||||
cancelConflicts(entry.m_command);
|
||||
entry.m_command.startRunning();
|
||||
m_children.addElement(entry);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Run Children
|
||||
for (int i = 0; i < m_children.size(); i++) {
|
||||
entry = m_children.elementAt(i);
|
||||
Command child = entry.m_command;
|
||||
if (entry.isTimedOut()) {
|
||||
child._cancel();
|
||||
}
|
||||
if (!child.run()) {
|
||||
child.removed();
|
||||
m_children.removeElementAt(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
void _end() {
|
||||
// Theoretically, we don't have to check this, but we do if teams override
|
||||
// the isFinished method
|
||||
if (m_currentCommandIndex != -1 && m_currentCommandIndex < m_commands.size()) {
|
||||
Command cmd = m_commands.elementAt(m_currentCommandIndex).m_command;
|
||||
cmd._cancel();
|
||||
cmd.removed();
|
||||
}
|
||||
|
||||
Enumeration children = m_children.elements();
|
||||
while (children.hasMoreElements()) {
|
||||
Command cmd = ((Entry) children.nextElement()).m_command;
|
||||
cmd._cancel();
|
||||
cmd.removed();
|
||||
}
|
||||
m_children.removeAllElements();
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodName")
|
||||
void _interrupted() {
|
||||
_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all the {@link Command Commands} in this group have been started and have
|
||||
* finished.
|
||||
*
|
||||
* <p> Teams may override this method, although they should probably reference super.isFinished()
|
||||
* if they do. </p>
|
||||
*
|
||||
* @return whether this {@link CommandGroup} is finished
|
||||
*/
|
||||
protected boolean isFinished() {
|
||||
return m_currentCommandIndex >= m_commands.size() && m_children.isEmpty();
|
||||
}
|
||||
|
||||
// Can be overwritten by teams
|
||||
protected void initialize() {
|
||||
}
|
||||
|
||||
// Can be overwritten by teams
|
||||
protected void execute() {
|
||||
}
|
||||
|
||||
// Can be overwritten by teams
|
||||
protected void end() {
|
||||
}
|
||||
|
||||
// Can be overwritten by teams
|
||||
protected void interrupted() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this group is interruptible. A command group will be uninterruptible if
|
||||
* {@link CommandGroup#setInterruptible(boolean) setInterruptable(false)} was called or if it is
|
||||
* currently running an uninterruptible command or child.
|
||||
*
|
||||
* @return whether or not this {@link CommandGroup} is interruptible.
|
||||
*/
|
||||
public synchronized boolean isInterruptible() {
|
||||
if (!super.isInterruptible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_currentCommandIndex != -1 && m_currentCommandIndex < m_commands.size()) {
|
||||
Command cmd = m_commands.elementAt(m_currentCommandIndex).m_command;
|
||||
if (!cmd.isInterruptible()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_children.size(); i++) {
|
||||
if (!m_children.elementAt(i).m_command.isInterruptible()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void cancelConflicts(Command command) {
|
||||
for (int i = 0; i < m_children.size(); i++) {
|
||||
Command child = m_children.elementAt(i).m_command;
|
||||
|
||||
Enumeration requirements = command.getRequirements();
|
||||
|
||||
while (requirements.hasMoreElements()) {
|
||||
Object requirement = requirements.nextElement();
|
||||
if (child.doesRequire((Subsystem) requirement)) {
|
||||
child._cancel();
|
||||
child.removed();
|
||||
m_children.removeElementAt(i--);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
|
||||
private static final int IN_SEQUENCE = 0;
|
||||
private static final int BRANCH_PEER = 1;
|
||||
private static final int BRANCH_CHILD = 2;
|
||||
private final Command m_command;
|
||||
private final int m_state;
|
||||
private final double m_timeout;
|
||||
|
||||
Entry(Command command, int state) {
|
||||
m_command = command;
|
||||
m_state = state;
|
||||
m_timeout = -1;
|
||||
}
|
||||
|
||||
Entry(Command command, int state, double timeout) {
|
||||
m_command = command;
|
||||
m_state = state;
|
||||
m_timeout = timeout;
|
||||
}
|
||||
|
||||
boolean isTimedOut() {
|
||||
if (m_timeout == -1) {
|
||||
return false;
|
||||
} else {
|
||||
double time = m_command.timeSinceInitialized();
|
||||
return time != 0 && time >= m_timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017. 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.command;
|
||||
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* A {@link ConditionalCommand} is a {@link Command} that starts one of two commands.
|
||||
*
|
||||
* <p>
|
||||
* A {@link ConditionalCommand} uses m_condition to determine whether it should run m_onTrue or
|
||||
* m_onFalse.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A {@link ConditionalCommand} adds the proper {@link Command} to the {@link Scheduler} during
|
||||
* {@link ConditionalCommand#initialize()} and then {@link ConditionalCommand#isFinished()} will
|
||||
* return true once that {@link Command} has finished executing.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If no {@link Command} is specified for m_onFalse, the occurrence of that condition will be a
|
||||
* no-op.
|
||||
* </p>
|
||||
*
|
||||
* @see Command
|
||||
* @see Scheduler
|
||||
*/
|
||||
public abstract class ConditionalCommand extends Command {
|
||||
/**
|
||||
* The Command to execute if {@link ConditionalCommand#condition()} returns true.
|
||||
*/
|
||||
private Command m_onTrue;
|
||||
|
||||
/**
|
||||
* The Command to execute if {@link ConditionalCommand#condition()} returns false.
|
||||
*/
|
||||
private Command m_onFalse;
|
||||
|
||||
/**
|
||||
* Stores command chosen by condition.
|
||||
*/
|
||||
private Command m_chosenCommand = null;
|
||||
|
||||
private void requireAll() {
|
||||
if (m_onTrue != null) {
|
||||
for (Enumeration e = m_onTrue.getRequirements(); e.hasMoreElements(); ) {
|
||||
requires((Subsystem) e.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_onFalse != null) {
|
||||
for (Enumeration e = m_onFalse.getRequirements(); e.hasMoreElements(); ) {
|
||||
requires((Subsystem) e.nextElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ConditionalCommand with given onTrue and onFalse Commands.
|
||||
*
|
||||
* <p>Users of this constructor should also override condition().
|
||||
*
|
||||
* @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
|
||||
*/
|
||||
public ConditionalCommand(Command onTrue) {
|
||||
this(onTrue, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ConditionalCommand with given onTrue and onFalse Commands.
|
||||
*
|
||||
* <p>Users of this constructor should also override condition().
|
||||
*
|
||||
* @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
|
||||
* @param onFalse The Command to execute if {@link ConditionalCommand#condition()} returns false
|
||||
*/
|
||||
public ConditionalCommand(Command onTrue, Command onFalse) {
|
||||
m_onTrue = onTrue;
|
||||
m_onFalse = onFalse;
|
||||
|
||||
requireAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ConditionalCommand with given name and onTrue and onFalse Commands.
|
||||
*
|
||||
* <p>Users of this constructor should also override condition().
|
||||
*
|
||||
* @param name the name for this command group
|
||||
* @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
|
||||
*/
|
||||
public ConditionalCommand(String name, Command onTrue) {
|
||||
this(name, onTrue, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ConditionalCommand with given name and onTrue and onFalse Commands.
|
||||
*
|
||||
* <p>Users of this constructor should also override condition().
|
||||
*
|
||||
* @param name the name for this command group
|
||||
* @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
|
||||
* @param onFalse The Command to execute if {@link ConditionalCommand#condition()} returns false
|
||||
*/
|
||||
public ConditionalCommand(String name, Command onTrue, Command onFalse) {
|
||||
super(name);
|
||||
m_onTrue = onTrue;
|
||||
m_onFalse = onFalse;
|
||||
|
||||
requireAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Condition to test to determine which Command to run.
|
||||
*
|
||||
* @return true if m_onTrue should be run or false if m_onFalse should be run.
|
||||
*/
|
||||
protected abstract boolean condition();
|
||||
|
||||
/**
|
||||
* Calls {@link ConditionalCommand#condition()} and runs the proper command.
|
||||
*/
|
||||
@Override
|
||||
protected void _initialize() {
|
||||
if (condition()) {
|
||||
m_chosenCommand = m_onTrue;
|
||||
} else {
|
||||
m_chosenCommand = m_onFalse;
|
||||
}
|
||||
|
||||
if (m_chosenCommand != null) {
|
||||
/*
|
||||
* This is a hack to make cancelling the chosen command inside a
|
||||
* CommandGroup work properly
|
||||
*/
|
||||
m_chosenCommand.clearRequirements();
|
||||
|
||||
m_chosenCommand.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _cancel() {
|
||||
if (m_chosenCommand != null && m_chosenCommand.isRunning()) {
|
||||
m_chosenCommand.cancel();
|
||||
}
|
||||
|
||||
super._cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFinished() {
|
||||
return m_chosenCommand != null && m_chosenCommand.isRunning()
|
||||
&& m_chosenCommand.isFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void interrupted() {
|
||||
if (m_chosenCommand != null && m_chosenCommand.isRunning()) {
|
||||
m_chosenCommand.cancel();
|
||||
}
|
||||
|
||||
super.interrupted();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.command;
|
||||
|
||||
/**
|
||||
* This exception will be thrown if a command is used illegally. There are several ways for this to
|
||||
* happen.
|
||||
*
|
||||
* <p> Basically, a command becomes "locked" after it is first started or added to a command group.
|
||||
* </p>
|
||||
*
|
||||
* <p> This exception should be thrown if (after a command has been locked) its requirements change,
|
||||
* it is put into multiple command groups, it is started from outside its command group, or it adds
|
||||
* a new child. </p>
|
||||
*/
|
||||
public class IllegalUseOfCommandException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Instantiates an {@link IllegalUseOfCommandException}.
|
||||
*/
|
||||
public IllegalUseOfCommandException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates an {@link IllegalUseOfCommandException} with the given message.
|
||||
*
|
||||
* @param message the message
|
||||
*/
|
||||
public IllegalUseOfCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2016-2017. 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.command;
|
||||
|
||||
/**
|
||||
* This command will execute once, then finish immediately afterward.
|
||||
*
|
||||
* <p>Subclassing {@link InstantCommand} is shorthand for returning true from
|
||||
* {@link Command isFinished}.
|
||||
*/
|
||||
public class InstantCommand extends Command {
|
||||
|
||||
public InstantCommand() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link InstantCommand InstantCommand} with the given name.
|
||||
* @param name the name for this command
|
||||
*/
|
||||
public InstantCommand(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
protected boolean isFinished() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.command;
|
||||
|
||||
/**
|
||||
* An element that is in a LinkedList.
|
||||
*/
|
||||
class LinkedListElement {
|
||||
private LinkedListElement m_next;
|
||||
private LinkedListElement m_previous;
|
||||
private Command m_data;
|
||||
|
||||
public LinkedListElement() {
|
||||
}
|
||||
|
||||
public void setData(Command newData) {
|
||||
m_data = newData;
|
||||
}
|
||||
|
||||
public Command getData() {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
public LinkedListElement getNext() {
|
||||
return m_next;
|
||||
}
|
||||
|
||||
public LinkedListElement getPrevious() {
|
||||
return m_previous;
|
||||
}
|
||||
|
||||
public void add(LinkedListElement listElement) {
|
||||
if (m_next == null) {
|
||||
m_next = listElement;
|
||||
m_next.m_previous = this;
|
||||
} else {
|
||||
m_next.m_previous = listElement;
|
||||
listElement.m_next = m_next;
|
||||
listElement.m_previous = this;
|
||||
m_next = listElement;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.EmptyIfStmt")
|
||||
public LinkedListElement remove() {
|
||||
if (m_previous == null && m_next == null) {
|
||||
// no-op
|
||||
} else if (m_next == null) {
|
||||
m_previous.m_next = null;
|
||||
} else if (m_previous == null) {
|
||||
m_next.m_previous = null;
|
||||
} else {
|
||||
m_next.m_previous = m_previous;
|
||||
m_previous.m_next = m_next;
|
||||
}
|
||||
LinkedListElement returnNext = m_next;
|
||||
m_next = null;
|
||||
m_previous = null;
|
||||
return returnNext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2008-2017. 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.command;
|
||||
|
||||
import edu.wpi.first.wpilibj.PIDController;
|
||||
import edu.wpi.first.wpilibj.PIDOutput;
|
||||
import edu.wpi.first.wpilibj.PIDSource;
|
||||
import edu.wpi.first.wpilibj.PIDSourceType;
|
||||
import edu.wpi.first.wpilibj.Sendable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
|
||||
/**
|
||||
* This class defines a {@link Command} which interacts heavily with a PID loop.
|
||||
*
|
||||
* <p> It provides some convenience methods to run an internal {@link PIDController} . It will also
|
||||
* start and stop said {@link PIDController} when the {@link PIDCommand} is first initialized and
|
||||
* ended/interrupted. </p>
|
||||
*/
|
||||
public abstract class PIDCommand extends Command implements Sendable {
|
||||
|
||||
/**
|
||||
* The internal {@link PIDController}.
|
||||
*/
|
||||
private final PIDController m_controller;
|
||||
/**
|
||||
* An output which calls {@link PIDCommand#usePIDOutput(double)}.
|
||||
*/
|
||||
private final PIDOutput m_output = this::usePIDOutput;
|
||||
/**
|
||||
* A source which calls {@link PIDCommand#returnPIDInput()}.
|
||||
*/
|
||||
private final PIDSource m_source = new PIDSource() {
|
||||
public void setPIDSourceType(PIDSourceType pidSource) {
|
||||
}
|
||||
|
||||
public PIDSourceType getPIDSourceType() {
|
||||
return PIDSourceType.kDisplacement;
|
||||
}
|
||||
|
||||
public double pidGet() {
|
||||
return returnPIDInput();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiates a {@link PIDCommand} that will use the given p, i and d values.
|
||||
*
|
||||
* @param name the name of the command
|
||||
* @param p the proportional value
|
||||
* @param i the integral value
|
||||
* @param d the derivative value
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public PIDCommand(String name, double p, double i, double d) {
|
||||
super(name);
|
||||
m_controller = new PIDController(p, i, d, m_source, m_output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will also space
|
||||
* the time between PID loop calculations to be equal to the given period.
|
||||
*
|
||||
* @param name the name
|
||||
* @param p the proportional value
|
||||
* @param i the integral value
|
||||
* @param d the derivative value
|
||||
* @param period the time (in seconds) between calculations
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public PIDCommand(String name, double p, double i, double d, double period) {
|
||||
super(name);
|
||||
m_controller = new PIDController(p, i, d, m_source, m_output, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will use the
|
||||
* class name as its name.
|
||||
*
|
||||
* @param p the proportional value
|
||||
* @param i the integral value
|
||||
* @param d the derivative value
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public PIDCommand(double p, double i, double d) {
|
||||
m_controller = new PIDController(p, i, d, m_source, m_output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a {@link PIDCommand} that will use the given p, i and d values. It will use the
|
||||
* class name as its name.. It will also space the time between PID loop calculations to be equal
|
||||
* to the given period.
|
||||
*
|
||||
* @param p the proportional value
|
||||
* @param i the integral value
|
||||
* @param d the derivative value
|
||||
* @param period the time (in seconds) between calculations
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public PIDCommand(double p, double i, double d, double period) {
|
||||
m_controller = new PIDController(p, i, d, m_source, m_output, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link PIDController} used by this {@link PIDCommand}. Use this if you would like
|
||||
* to fine tune the pid loop.
|
||||
*
|
||||
* @return the {@link PIDController} used by this {@link PIDCommand}
|
||||
*/
|
||||
protected PIDController getPIDController() {
|
||||
return m_controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("MethodName")
|
||||
void _initialize() {
|
||||
m_controller.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("MethodName")
|
||||
void _end() {
|
||||
m_controller.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("MethodName")
|
||||
void _interrupted() {
|
||||
_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value to the setpoint. If {@link PIDCommand#setInputRange(double, double)
|
||||
* setInputRange(...)} was used, then the bounds will still be honored by this method.
|
||||
*
|
||||
* @param deltaSetpoint the change in the setpoint
|
||||
*/
|
||||
public void setSetpointRelative(double deltaSetpoint) {
|
||||
setSetpoint(getSetpoint() + deltaSetpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setpoint to the given value. If {@link PIDCommand#setInputRange(double, double)
|
||||
* setInputRange(...)} was called, then the given setpoint will be trimmed to fit within the
|
||||
* range.
|
||||
*
|
||||
* @param setpoint the new setpoint
|
||||
*/
|
||||
protected void setSetpoint(double setpoint) {
|
||||
m_controller.setSetpoint(setpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the setpoint.
|
||||
*
|
||||
* @return the setpoint
|
||||
*/
|
||||
protected double getSetpoint() {
|
||||
return m_controller.getSetpoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current position.
|
||||
*
|
||||
* @return the current position
|
||||
*/
|
||||
protected double getPosition() {
|
||||
return returnPIDInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum and minimum values expected from the input and setpoint.
|
||||
*
|
||||
* @param minimumInput the minimum value expected from the input and setpoint
|
||||
* @param maximumInput the maximum value expected from the input and setpoint
|
||||
*/
|
||||
protected void setInputRange(double minimumInput, double maximumInput) {
|
||||
m_controller.setInputRange(minimumInput, maximumInput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input for the pid loop.
|
||||
*
|
||||
* <p>It returns the input for the pid loop, so if this command was based off of a gyro, then it
|
||||
* should return the angle of the gyro.
|
||||
*
|
||||
* <p>All subclasses of {@link PIDCommand} must override this method.
|
||||
*
|
||||
* <p>This method will be called in a different thread then the {@link Scheduler} thread.
|
||||
*
|
||||
* @return the value the pid loop should use as input
|
||||
*/
|
||||
protected abstract double returnPIDInput();
|
||||
|
||||
/**
|
||||
* Uses the value that the pid loop calculated. The calculated value is the "output" parameter.
|
||||
* This method is a good time to set motor values, maybe something along the lines of
|
||||
* <code>driveline.tankDrive(output, -output)</code>
|
||||
*
|
||||
* <p>All subclasses of {@link PIDCommand} must override this method.
|
||||
*
|
||||
* <p>This method will be called in a different thread then the {@link Scheduler} thread.
|
||||
*
|
||||
* @param output the value the pid loop calculated
|
||||
*/
|
||||
protected abstract void usePIDOutput(double output);
|
||||
|
||||
public String getSmartDashboardType() {
|
||||
return "PIDCommand";
|
||||
}
|
||||
|
||||
public void initTable(ITable table) {
|
||||
m_controller.initTable(table);
|
||||
super.initTable(table);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user