Skip to content

Commit 68f09ad

Browse files
authored
Merge pull request #94 from DeepBlueRobotics/fix-use-of-noncompliant-names-for-devices-and-data
Use spec compliant names for devices and data to fix issue #93.
2 parents 3f7e6df + 68bc5f1 commit 68f09ad

15 files changed

Lines changed: 170 additions & 126 deletions

src/main/java/org/carlmontrobotics/lib199/Lib199Subsystem.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,24 @@ public void simulationPeriodic() {
8484
periodicSimulationMethods.forEach(RUN_RUNNABLE);
8585
}
8686

87-
public void asyncPeriodic() {
87+
public synchronized void asyncPeriodic() {
8888
asyncPeriodicMethods.forEach(RUN_RUNNABLE);
8989
asyncPeriodicSimulationMethods.forEach(RUN_RUNNABLE);
9090
}
9191

92+
/**
93+
* Unregisters all Runnables registered with registerAsyncPeriodic() and
94+
* registerAsyncSimulationPeriodic(). Blocks until any currently registered
95+
* Runnables have finished running. This is particularly useful for ensuring
96+
* that Runnables registered in one test don't interfere with other tests.
97+
*/
98+
public static void unregisterAllAsync() {
99+
synchronized (INSTANCE) {
100+
asyncPeriodicMethods.clear();
101+
asyncPeriodicSimulationMethods.clear();
102+
}
103+
}
104+
92105
private Lib199Subsystem() {}
93106

94107
}

src/main/java/org/carlmontrobotics/lib199/sim/MockSparkBase.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class MockSparkBase extends MockedMotorBase {
3636
private SparkAbsoluteEncoder absoluteEncoder = null;
3737
private MockedEncoder alternateEncoder = null;
3838
private SparkAnalogSensor analogSensor = null;
39+
private final String name;
3940

4041
/**
4142
* Initializes a new {@link SimDevice} with the given parameters and creates the necessary sim values, and
@@ -44,14 +45,16 @@ public class MockSparkBase extends MockedMotorBase {
4445
* @param port the port to associate this {@code MockSparkMax} with. Will be used to create the {@link SimDevice} and facilitate motor following.
4546
* @param type the type of the simulated motor. If this is set to {@link MotorType#kBrushless}, the builtin encoder simulation will be configured
4647
* to follow the inversion state of the motor and its {@code setInverted} method will be disabled.
47-
* @param name the name of the type of controller ("SparkMax" or "SparkFlex")
48+
* @param name the name of the type of controller ("CANSparkMax" or "CANSparkFlex")
49+
* @param countsPerRev the number of counts per revolution of this controller's built-in encoder.
4850
*/
49-
public MockSparkBase(int port, MotorType type, String name) {
51+
public MockSparkBase(int port, MotorType type, String name, int countsPerRev) {
5052
super(name, port);
5153
this.type = type;
54+
this.name = name;
5255

5356
if(type == MotorType.kBrushless) {
54-
encoder = new MockedEncoder(SimDevice.create(device.getName() + "_RelativeEncoder"), MockedEncoder.NEO_BUILTIN_ENCODER_CPR, false) {
57+
encoder = new MockedEncoder(SimDevice.create("CANEncoder:" + name, port), countsPerRev, false, false) {
5558
@Override
5659
public REVLibError setInverted(boolean inverted) {
5760
System.err.println(
@@ -60,7 +63,7 @@ public REVLibError setInverted(boolean inverted) {
6063
}
6164
};
6265
} else {
63-
encoder = new MockedEncoder(SimDevice.create(device.getName() + "_RelativeEncoder"), MockedEncoder.NEO_BUILTIN_ENCODER_CPR, false);
66+
encoder = new MockedEncoder(SimDevice.create("CANEncoder:" + name, port), countsPerRev, false, false);
6467
}
6568

6669
pidControllerImpl = new MockedSparkMaxPIDController(this);
@@ -192,9 +195,14 @@ public void close() {
192195
* @return the simulated encoder
193196
*/
194197
public synchronized SparkAbsoluteEncoder getAbsoluteEncoder(SparkAbsoluteEncoder.Type encoderType) {
195-
System.err.println("WARNING: An absolute encoder was created for a simulated Spark Max. Currently, the only way to specify the CPR is to use the REVHardwareClient. A CPR of " + MockedEncoder.NEO_BUILTIN_ENCODER_CPR + " will be assumed.");
196198
if(absoluteEncoder == null) {
197-
MockedEncoder absoluteEncoderImpl = new MockedEncoder(SimDevice.create(device.getName() + "_AbsoluteEncoder"), MockedEncoder.NEO_BUILTIN_ENCODER_CPR, true);
199+
MockedEncoder absoluteEncoderImpl = new MockedEncoder(SimDevice.create("CANDutyCycle:" + name, port), 0, false, true) {
200+
@Override
201+
public double getVelocity() {
202+
// A SparkAbsoluteEncoder returns a velocity in rps, not rpm.
203+
return super.getVelocity() / 60.0;
204+
}
205+
};
198206
absoluteEncoder = Mocks.createMock(SparkAbsoluteEncoder.class, absoluteEncoderImpl, new REVLibErrorAnswer());
199207
}
200208
return absoluteEncoder;
@@ -223,7 +231,7 @@ public RelativeEncoder getAlternateEncoder(int countsPerRev) {
223231
*/
224232
public synchronized RelativeEncoder getAlternateEncoder(SparkMaxAlternateEncoder.Type encoderType, int countsPerRev) {
225233
if(alternateEncoder == null) {
226-
alternateEncoder = new MockedEncoder(SimDevice.create(device.getName() + "_AlternateEncoder"), countsPerRev, false);
234+
alternateEncoder = new MockedEncoder(SimDevice.create("CANEncoder:%s[%d]-alternate".formatted(name, port)), 0, false, false);
227235
}
228236
return alternateEncoder;
229237
}
@@ -239,7 +247,7 @@ public synchronized RelativeEncoder getAlternateEncoder(SparkMaxAlternateEncoder
239247
*/
240248
public synchronized SparkAnalogSensor getAnalog(SparkAnalogSensor.Mode mode) {
241249
if(analogSensor == null) {
242-
MockedEncoder analogSensorImpl = new MockedEncoder(SimDevice.create(device.getName() + "_AnalogSensor"), MockedEncoder.ANALOG_SENSOR_MAX_VOLTAGE, true);
250+
MockedEncoder analogSensorImpl = new MockedEncoder(SimDevice.create("CANAIn:" + name, port), 0, true, true);
243251
analogSensor = Mocks.createMock(SparkAnalogSensor.class, analogSensorImpl, new REVLibErrorAnswer());
244252
}
245253
return analogSensor;
@@ -268,6 +276,10 @@ public REVLibError setIdleMode(IdleMode mode) {
268276
return REVLibError.kOk;
269277
}
270278

279+
public double getOutputCurrent() {
280+
return getCurrentDraw();
281+
}
282+
271283
@Override
272284
public void disable() {
273285
// CANSparkBase sets the motor speed to zero rather than actually disabling the motor

src/main/java/org/carlmontrobotics/lib199/sim/MockSparkFlex.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
public class MockSparkFlex extends MockSparkBase {
1010

1111
public MockSparkFlex(int port, MotorType type) {
12-
super(port, type, "SparkFlex");
12+
super(port, type, "CANSparkFlex", 7168);
1313
}
1414

1515
public static CANSparkFlex createMockSparkFlex(int portPWM, MotorType type) {

src/main/java/org/carlmontrobotics/lib199/sim/MockSparkMax.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
public class MockSparkMax extends MockSparkBase {
1010

1111
public MockSparkMax(int port, MotorType type) {
12-
super(port, type, "SparkMax");
12+
super(port, type, "CANSparkMax", 42);
1313
}
1414

1515
public static CANSparkMax createMockSparkMax(int portPWM, MotorType type) {

src/main/java/org/carlmontrobotics/lib199/sim/MockedCANCoder.java

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
import com.ctre.phoenix6.hardware.CANcoder;
66
import com.ctre.phoenix6.sim.CANcoderSimState;
77

8-
import org.carlmontrobotics.lib199.Lib199Subsystem;
9-
108
import edu.wpi.first.hal.HALValue;
9+
import edu.wpi.first.hal.SimBoolean;
1110
import edu.wpi.first.hal.SimDevice;
1211
import edu.wpi.first.hal.SimDevice.Direction;
1312
import edu.wpi.first.hal.simulation.SimValueCallback;
@@ -16,39 +15,27 @@
1615

1716
public class MockedCANCoder {
1817

19-
public static final double kCANCoderCPR = 4096;
20-
2118
private static final HashMap<Integer, MockedCANCoder> sims = new HashMap<>();
2219

2320
private int port;
2421
private SimDevice device;
2522
private SimDeviceSim deviceSim;
2623
private SimDouble position; // Rotations - Continuous
27-
private SimDouble gearing;
2824
private CANcoderSimState sim;
2925

3026
public MockedCANCoder(CANcoder canCoder) {
3127
port = canCoder.getDeviceID();
32-
device = SimDevice.create("CANCoder", port);
33-
position = device.createDouble("count", Direction.kInput, 0);
34-
gearing = device.createDouble("gearing", Direction.kOutput, 1);
28+
device = SimDevice.create("CANDutyCycle:CANCoder", port);
29+
position = device.createDouble("position", Direction.kInput, 0);
3530
sim = canCoder.getSimState();
36-
deviceSim = new SimDeviceSim("CANCoder", port);
31+
deviceSim = new SimDeviceSim("CANDutyCycle:CANCoder", port);
3732
deviceSim.registerValueChangedCallback(position, new SimValueCallback() {
3833
@Override
3934
public void callback(String name, int handle, int direction, HALValue value) {
40-
sim.setRawPosition(value.getDouble() / kCANCoderCPR);
35+
sim.setRawPosition(value.getDouble());
4136
}
4237
}, true);
4338
sims.put(port, this);
4439
}
4540

46-
public void setGearing(double gearing) {
47-
this.gearing.set(gearing);
48-
}
49-
50-
public static void setGearing(int port, double gearing) {
51-
if(sims.containsKey(port)) sims.get(port).setGearing(gearing);
52-
}
53-
5441
}

src/main/java/org/carlmontrobotics/lib199/sim/MockedEncoder.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.revrobotics.REVLibError;
66
import com.revrobotics.RelativeEncoder;
77

8+
import edu.wpi.first.hal.SimBoolean;
89
import edu.wpi.first.hal.SimDevice;
910
import edu.wpi.first.hal.SimDevice.Direction;
1011
import edu.wpi.first.math.MathUtil;
@@ -22,13 +23,14 @@
2223
*/
2324
public class MockedEncoder implements AbsoluteEncoder, AnalogInput, AutoCloseable, RelativeEncoder {
2425

25-
public static final int NEO_BUILTIN_ENCODER_CPR = 42;
2626
public static final double ANALOG_SENSOR_MAX_VOLTAGE = 3.3;
2727

2828
public final SimDevice device;
2929
protected final SimDouble position;
3030
protected final SimDouble velocity;
31-
protected final double countsOrVoltsPerRev;
31+
protected final SimDouble voltage;
32+
protected final SimBoolean init;
33+
protected final int countsPerRev;
3234
protected final boolean absolute;
3335
protected double positionConversionFactor = 1.0;
3436
protected double velocityConversionFactor = 1.0;
@@ -37,17 +39,24 @@ public class MockedEncoder implements AbsoluteEncoder, AnalogInput, AutoCloseabl
3739

3840
/**
3941
* @param device The device to retrieve position and velocity data from
40-
* @param countsOrVoltsPerRev The cpr of the simulated encoder
42+
* @param countsPerRev The value that this.getCountsPerRevolution() should return
43+
* @param analog Whether the encoder is an analog sensor
4144
* @param absolute Whether the encoder is an absolute encoder.
4245
* This flag caps the position to one rotation via. {@link MathUtil#inputModulus(double, double, double)},
4346
* disables {@link #setPosition(double)}, and enables {@link #setZeroOffset(double)}.
4447
*/
45-
public MockedEncoder(SimDevice device, double countsOrVoltsPerRev, boolean absolute) {
48+
public MockedEncoder(SimDevice device, int countsPerRev, boolean analog, boolean absolute) {
4649
this.device = device;
47-
position = device.createDouble("Position", Direction.kInput, 0);
48-
velocity = device.createDouble("Velocity", Direction.kInput, 0);
49-
this.countsOrVoltsPerRev = countsOrVoltsPerRev;
50+
position = device.createDouble("position", Direction.kInput, 0); // Rotations
51+
velocity = device.createDouble("velocity", Direction.kInput, 0); // Rotations per *second*
52+
if (analog) {
53+
voltage = device.createDouble("voltage", Direction.kInput, 0);
54+
} else {
55+
voltage = null;
56+
}
57+
this.countsPerRev = countsPerRev;
5058
this.absolute = absolute;
59+
init = device.createBoolean("init", Direction.kOutput, true);
5160
}
5261

5362
@Override
@@ -74,14 +83,15 @@ public int getMeasurementPeriod() {
7483

7584
@Override
7685
public int getCountsPerRevolution() {
77-
return (int)countsOrVoltsPerRev;
86+
return countsPerRev;
7887
}
7988

8089
/**
8190
* @return The current position of the encoder, not accounting for the position offset ({@link #setPosition(double)} and {@link #setZeroOffset(double)})
8291
*/
8392
public double getRawPosition() {
84-
return position.get() * (inverted ? -1 : 1) * positionConversionFactor / countsOrVoltsPerRev;
93+
double rotationsOrVolts = voltage != null ? voltage.get() : position.get();
94+
return rotationsOrVolts * (inverted ? -1 : 1) * positionConversionFactor;
8595
}
8696

8797
@Override
@@ -95,7 +105,7 @@ public double getPosition() {
95105

96106
@Override
97107
public double getVelocity() {
98-
return velocity.get() * (inverted ? -1 : 1) * velocityConversionFactor / countsOrVoltsPerRev;
108+
return velocity.get() * 60 * (inverted ? -1 : 1) * velocityConversionFactor;
99109
}
100110

101111
@Override
@@ -160,14 +170,13 @@ public double getZeroOffset() {
160170

161171
@Override
162172
public void close() {
173+
init.set(false);
163174
device.close();
164175
}
165176

166177
@Override
167178
public double getVoltage() {
168-
// This method only makes sense for an analog sensor and for an analog sensor,
169-
// position.get() is supposed to return volts.
170-
return position.get();
179+
return voltage.get();
171180
}
172181

173182
}

src/main/java/org/carlmontrobotics/lib199/sim/MockedMotorBase.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public abstract class MockedMotorBase implements AutoCloseable, MotorController,
2626
public final SimDouble neutralDeadband;
2727
public final SimBoolean brakeModeEnabled;
2828
public final SimDouble currentDraw;
29+
public final SimDouble busVoltage;
2930
protected SlewRateLimiter rampRateLimiter = null;
3031
protected boolean isInverted = false;
3132
protected boolean disabled = false;
@@ -42,12 +43,13 @@ public abstract class MockedMotorBase implements AutoCloseable, MotorController,
4243
* @param port the device port to pass to {@link SimDevice#create}
4344
*/
4445
public MockedMotorBase(String type, int port) {
45-
device = SimDevice.create(type, port);
46+
device = SimDevice.create("CANMotor:" + type, port);
4647
this.port = port;
47-
speed = device.createDouble("Speed", Direction.kOutput, 0.0);
48-
neutralDeadband = device.createDouble("Neutral Deadband", Direction.kOutput, 0.04);
49-
brakeModeEnabled = device.createBoolean("Brake Mode", Direction.kOutput, true);
50-
currentDraw = device.createDouble("Current Draw", Direction.kInput, 0.0);
48+
speed = device.createDouble("percentOutput", Direction.kOutput, 0.0);
49+
neutralDeadband = device.createDouble("neutralDeadband", Direction.kOutput, 0.04);
50+
brakeModeEnabled = device.createBoolean("brakeMode", Direction.kOutput, true);
51+
currentDraw = device.createDouble("motorCurrent", Direction.kInput, 0.0);
52+
busVoltage = device.createDouble("busVoltage", Direction.kInput, defaultNominalVoltage);
5153
}
5254

5355
/**
@@ -137,7 +139,7 @@ public void doEnableVoltageCompensation(double nominalVoltage) {
137139
}
138140

139141
public void doDisableVoltageCompensation() {
140-
voltageCompensationNominalVoltage = defaultNominalVoltage;
142+
voltageCompensationNominalVoltage = busVoltage.get();
141143
setRampRate(getRampRate()); // Update the ramp rate to account for the new nominal voltage
142144
}
143145

@@ -151,7 +153,7 @@ public double getCurrentDraw() {
151153

152154
public void updateRequestedSpeed() {
153155
double percent = getRequestedSpeed();
154-
percent *= voltageCompensationNominalVoltage / defaultNominalVoltage;
156+
percent *= voltageCompensationNominalVoltage / busVoltage.get();
155157
percent *= isInverted ? -1.0 : 1.0;
156158
requestedSpeedPercent = percent;
157159
}

src/main/java/org/carlmontrobotics/lib199/sim/MockedPlayingWithFusionTimeOfFlight.java

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.playingwithfusion.TimeOfFlight;
99
import com.playingwithfusion.TimeOfFlight.RangingMode;
1010

11+
import edu.wpi.first.hal.SimBoolean;
1112
import edu.wpi.first.hal.SimDevice;
1213
import edu.wpi.first.hal.SimDevice.Direction;
1314
import edu.wpi.first.hal.SimDouble;
@@ -17,33 +18,39 @@
1718
public class MockedPlayingWithFusionTimeOfFlight implements AutoCloseable {
1819

1920
private int port;
20-
private SimDevice device;
21+
private SimDevice rangeDevice;
22+
private SimDevice ambientLightLevelDevice;
2123
private SimDouble range, rangeSigma, sampleTime, ambientLightLevel;
24+
private SimBoolean rangeDeviceInit, ambientLightLevelDeviceInit;
2225
private SimInt roiLeft, roiTop, roiRight, roiBottom;
2326
private SimEnum status;
2427
private SimEnum rangingMode;
2528

2629
public MockedPlayingWithFusionTimeOfFlight(int portNumber) {
2730
port = portNumber;
28-
device = SimDevice.create("PlayingWithFusionTimeOfFlight", port);
29-
range = device.createDouble("range", Direction.kInput, 0);
30-
rangeSigma = device.createDouble("rangeSigma", Direction.kInput, 1);
31-
sampleTime = device.createDouble("sampleTime", Direction.kBidir, 24);
31+
rangeDevice = SimDevice.create("CANAIn:PlayingWithFusionTimeOfFlight[%d]-rangeVoltsIsMM".formatted(port));
32+
range = rangeDevice.createDouble("voltage", Direction.kInput, 0); // Millimeters
33+
rangeSigma = rangeDevice.createDouble("rangeSigma", Direction.kInput, 1); // Millimeters
34+
sampleTime = rangeDevice.createDouble("sampleTime", Direction.kBidir, 24); // Milliseconds
3235

3336
// Note: default ambientLightLevel of 0.005*16*16 Mcps is typical for office lighting per the vl5311x datasheet:
3437
// https://www.playingwithfusion.com/include/getfile.php?fileid=7073
35-
ambientLightLevel = device.createDouble("ambientLightLevel", Direction.kInput, 0.005*16*16);
38+
ambientLightLevelDevice = SimDevice.create("CANAIn:PlayingWithFusionTimeOfFlight[%d]-ambientLightLevelVoltsIsMcps".formatted(port));
39+
ambientLightLevel = ambientLightLevelDevice.createDouble("voltage", Direction.kInput, 0.005*16*16);
3640

3741
String[] statusNames = Arrays.stream(Status.values()).map(Status::name).toArray(String[]::new);
38-
status = device.createEnum("status", Direction.kInput, statusNames, Status.Invalid.ordinal());
42+
status = rangeDevice.createEnum("status", Direction.kInput, statusNames, Status.Invalid.ordinal());
3943

4044
String[] rangingModeNames = Arrays.stream(RangingMode.values()).map(RangingMode::name).toArray(String[]::new);
41-
rangingMode = device.createEnum("rangingMode", Direction.kInput, rangingModeNames, RangingMode.Short.ordinal());
45+
rangingMode = rangeDevice.createEnum("rangingMode", Direction.kInput, rangingModeNames, RangingMode.Short.ordinal());
4246

43-
roiLeft = device.createInt("roiLeft", Direction.kOutput, 0);
44-
roiTop = device.createInt("roiTop", Direction.kOutput, 0);
45-
roiRight = device.createInt("roiRight", Direction.kOutput, 15);
46-
roiBottom = device.createInt("roiBottom", Direction.kOutput, 15);
47+
roiLeft = rangeDevice.createInt("roiLeft", Direction.kOutput, 0);
48+
roiTop = rangeDevice.createInt("roiTop", Direction.kOutput, 0);
49+
roiRight = rangeDevice.createInt("roiRight", Direction.kOutput, 15);
50+
roiBottom = rangeDevice.createInt("roiBottom", Direction.kOutput, 15);
51+
52+
rangeDeviceInit = rangeDevice.createBoolean("init", Direction.kOutput, true);
53+
ambientLightLevelDeviceInit = ambientLightLevelDevice.createBoolean("init", Direction.kOutput, true);
4754
}
4855

4956
public static TimeOfFlight createMock(int portNumber) {
@@ -96,6 +103,9 @@ public void setRangingMode(RangingMode newMode, double newSampleTime) {
96103

97104
@Override
98105
public void close() {
99-
device.close();
106+
rangeDeviceInit.set(false);
107+
rangeDevice.close();
108+
ambientLightLevelDeviceInit.set(false);
109+
ambientLightLevelDevice.close();
100110
}
101111
}

0 commit comments

Comments
 (0)