Skip to content

Commit e67ef1e

Browse files
authored
Merge branch 'master' into update-libs
2 parents 07dc352 + ab4f4e2 commit e67ef1e

13 files changed

Lines changed: 142 additions & 56 deletions

File tree

.github/workflows/release.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Create Release
2+
3+
on:
4+
push:
5+
tags:
6+
- '[0-9]+.[0-9]+.[0-9]+'
7+
8+
jobs:
9+
release:
10+
name: Release Tag
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: write
14+
steps:
15+
- name: Create Release
16+
env:
17+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18+
tag: ${{ github.ref_name }}
19+
run: |
20+
gh release create "$tag" --repo="$GITHUB_REPOSITORY" --title="$tag" --generate-notes

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.revrobotics.RelativeEncoder;
55
import com.revrobotics.SparkMaxPIDController;
66

7+
@Deprecated
78
public class CachedSparkMax extends CANSparkMax {
89

910
private RelativeEncoder encoder;

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

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
11
package org.carlmontrobotics.lib199;
22

3-
import java.util.ArrayList;
3+
import java.util.concurrent.CopyOnWriteArrayList;
44
import java.util.function.Consumer;
55

6+
import edu.wpi.first.wpilibj.RobotBase;
67
import edu.wpi.first.wpilibj2.command.Subsystem;
78

89
public class Lib199Subsystem implements Subsystem {
910

1011
private static final Lib199Subsystem INSTANCE = new Lib199Subsystem();
11-
private static final ArrayList<Runnable> periodicMethods = new ArrayList<>();
12-
private static final ArrayList<Runnable> periodicSimulationMethods = new ArrayList<>();
12+
private static final CopyOnWriteArrayList<Runnable> periodicMethods = new CopyOnWriteArrayList<>();
13+
private static final CopyOnWriteArrayList<Runnable> periodicSimulationMethods = new CopyOnWriteArrayList<>();
14+
private static final CopyOnWriteArrayList<Runnable> asyncPeriodicMethods = new CopyOnWriteArrayList<>();
15+
private static final CopyOnWriteArrayList<Runnable> asyncPeriodicSimulationMethods = new CopyOnWriteArrayList<>();
1316
private static final Consumer<Runnable> RUN_RUNNABLE = Runnable::run;
1417

18+
private static final Thread asyncPeriodicThread;
19+
20+
public static final long asyncSleepTime = 20;
21+
1522
static {
1623
ensureRegistered();
24+
25+
asyncPeriodicThread = new Thread(() -> {
26+
while(true) {
27+
INSTANCE.asyncPeriodic();
28+
try {
29+
Thread.sleep(asyncSleepTime);
30+
} catch(InterruptedException e) {}
31+
}
32+
});
33+
asyncPeriodicThread.setDaemon(true);
34+
asyncPeriodicThread.start();
1735
}
18-
36+
1937
private static boolean registered = false;
2038

2139
private static void ensureRegistered() {
@@ -30,10 +48,27 @@ public static void registerPeriodic(Runnable method) {
3048
periodicMethods.add(method);
3149
}
3250

51+
@Deprecated
52+
/**
53+
* @deprecated Use registerSimulationPeriodic
54+
* @param method
55+
*/
3356
public static void simulationPeriodic(Runnable method) {
57+
registerSimulationPeriodic(method);
58+
}
59+
60+
public static void registerSimulationPeriodic(Runnable method) {
3461
periodicSimulationMethods.add(method);
3562
}
3663

64+
public static void registerAsyncPeriodic(Runnable method) {
65+
asyncPeriodicMethods.add(method);
66+
}
67+
68+
public static void registerAsyncSimulationPeriodic(Runnable method) {
69+
if(RobotBase.isSimulation()) asyncPeriodicSimulationMethods.add(method);
70+
}
71+
3772
@Override
3873
public void periodic() {
3974
periodicMethods.forEach(RUN_RUNNABLE);
@@ -44,6 +79,11 @@ public void simulationPeriodic() {
4479
periodicSimulationMethods.forEach(RUN_RUNNABLE);
4580
}
4681

82+
public void asyncPeriodic() {
83+
asyncPeriodicMethods.forEach(RUN_RUNNABLE);
84+
asyncPeriodicSimulationMethods.forEach(RUN_RUNNABLE);
85+
}
86+
4787
private Lib199Subsystem() {}
4888

4989
}

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
import java.lang.reflect.Modifier;
77
import java.util.ArrayList;
88
import java.util.Arrays;
9-
import java.util.Collections;
109
import java.util.HashMap;
11-
import java.util.List;
10+
import java.util.concurrent.CopyOnWriteArrayList;
1211
import java.util.function.Consumer;
1312
import java.util.function.Predicate;
1413
import java.util.stream.Collectors;
@@ -21,10 +20,10 @@
2120

2221
public final class Mocks {
2322

24-
private static final List<WeakReference<Object>> MOCKS = Collections.synchronizedList(new ArrayList<>());
23+
private static final CopyOnWriteArrayList<WeakReference<Object>> MOCKS = new CopyOnWriteArrayList<>();
2524
private static final Predicate<WeakReference<?>> IS_REFERENCE_CLEARED = reference -> reference.get() == null;
2625
private static final Consumer<WeakReference<Object>> CLEAR_INVOCATIONS_ON_REFERENCED_MOCK = reference -> Mockito.clearInvocations(reference.get());
27-
private static final Predicate<WeakReference<Object>> CLEAR_INVOCATIONS_ON_REFERENCED_MOCK_IF_REFERNCE_NOT_CLEARED = reference -> {
26+
private static final Predicate<WeakReference<Object>> CLEAR_INVOCATIONS_ON_REFERENCED_MOCK_IF_REFERENCE_NOT_CLEARED = reference -> {
2827
if(IS_REFERENCE_CLEARED.test(reference)) return true;
2928
CLEAR_INVOCATIONS_ON_REFERENCED_MOCK.accept(reference);
3029
return false;
@@ -37,7 +36,7 @@ public final class Mocks {
3736
// 2) Garbage collected references are removed
3837
// 3) Mock is garbage collected
3938
// 4) Mock invocations are cleared -> throws NullPointerException
40-
Lib199Subsystem.registerPeriodic(() -> MOCKS.removeIf(CLEAR_INVOCATIONS_ON_REFERENCED_MOCK_IF_REFERNCE_NOT_CLEARED));
39+
Lib199Subsystem.registerAsyncPeriodic(() -> MOCKS.removeIf(CLEAR_INVOCATIONS_ON_REFERENCED_MOCK_IF_REFERENCE_NOT_CLEARED));
4140
}
4241

4342
/**
@@ -61,7 +60,7 @@ public static <T, U> T createMock(Class<T> classToMock, U implClass, Class<?>...
6160
* @param <U> the class type which will be used to provide method implementations
6261
* @param classToMock the class type which will be mocked
6362
* @param implClass the object to which to try to forward method calls
64-
* @param forwardUnknownCalls whether methods which are not overriden will call their real methods
63+
* @param forwardUnknownCalls whether methods which are not overridden will call their real methods
6564
* @param interfaces a list of interfaces which the mocked object should extend
6665
* @return an instance of <code>T</code> in which some or all of the classes methods are replaced with a mocked implementation from <code>U</code>
6766
* @see #createMock(Class, Object, Class...)
@@ -77,7 +76,7 @@ public static <T, U> T createMock(Class<T> classToMock, U implClass, boolean for
7776
* @param <U> the class type which will be used to provide method implementations
7877
* @param classToMock the class type which will be mocked
7978
* @param implClass the object to which to try to forward method calls
80-
* @param defaultAnswer The answer to use when no overriden implementation is found
79+
* @param defaultAnswer The answer to use when no overridden implementation is found
8180
* @param interfaces a list of interfaces which the mocked object should extend
8281
* @return an instance of <code>T</code> in which some or all of the classes methods are replaced with a mocked implementation from <code>U</code>
8382
* @see #createMock(Class, Object, Class...)

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

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,9 @@ public static CANSparkMax createSparkMax(int id, int temperatureLimit) {
120120
public static CANSparkMax createSparkMax(int id, MotorConfig config) {
121121
CANSparkMax spark;
122122
if (RobotBase.isReal()) {
123-
spark = new CachedSparkMax(id, CANSparkMaxLowLevel.MotorType.kBrushless);
124-
if (spark.getFirmwareVersion() == 0) {
125-
spark.close();
126-
System.err.println("SparkMax on port: " + id + " is not connected!");
127-
return MotorErrors.createDummySparkMax();
128-
}
123+
spark = new CANSparkMax(id, CANSparkMaxLowLevel.MotorType.kBrushless);
129124
} else {
130-
spark = MockSparkMax.createMockSparkMax(id, CANSparkMaxLowLevel.MotorType.kBrushless);
125+
spark = MockSparkMax.createMockSparkMax(id, CANSparkMaxLowLevel.MotorType.kBrushless);
131126
}
132127

133128
MotorErrors.reportSparkMaxTemp(spark, config.temperatureLimit);
@@ -171,7 +166,7 @@ public static UsbCamera configureCamera() {
171166
}
172167

173168
/**
174-
* This method is equivilent to calling {@link #configureCamera()} {@code numCameras} times.
169+
* This method is equivalent to calling {@link #configureCamera()} {@code numCameras} times.
175170
* The last camera will be set as the primary Camera feed.
176171
* To change it, call {@code CameraServer.getServer().setSource()}.
177172
*

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.carlmontrobotics.lib199;
22

3-
import java.util.ArrayList;
43
import java.util.Arrays;
5-
import java.util.HashMap;
4+
import java.util.concurrent.ConcurrentHashMap;
5+
import java.util.concurrent.CopyOnWriteArrayList;
66

77
import com.ctre.phoenix.ErrorCode;
88
import com.revrobotics.CANSparkMax;
@@ -13,14 +13,14 @@
1313

1414
public final class MotorErrors {
1515

16-
private static final HashMap<Integer, CANSparkMax> temperatureSparks = new HashMap<>();
17-
private static final HashMap<Integer, Integer> sparkTemperatureLimits = new HashMap<>();
18-
private static final ArrayList<Integer> overheatedSparks = new ArrayList<>();
19-
private static final HashMap<CANSparkMax, Short> flags = new HashMap<>();
20-
private static final HashMap<CANSparkMax, Short> stickyFlags = new HashMap<>();
16+
private static final ConcurrentHashMap<Integer, CANSparkMax> temperatureSparks = new ConcurrentHashMap<>();
17+
private static final ConcurrentHashMap<Integer, Integer> sparkTemperatureLimits = new ConcurrentHashMap<>();
18+
private static final CopyOnWriteArrayList<Integer> overheatedSparks = new CopyOnWriteArrayList<>();
19+
private static final ConcurrentHashMap<CANSparkMax, Short> flags = new ConcurrentHashMap<>();
20+
private static final ConcurrentHashMap<CANSparkMax, Short> stickyFlags = new ConcurrentHashMap<>();
2121

2222
static {
23-
Lib199Subsystem.registerPeriodic(MotorErrors::doReportSparkMaxTemp);
23+
Lib199Subsystem.registerAsyncPeriodic(MotorErrors::doReportSparkMaxTemp);
2424
}
2525

2626
public static void reportError(ErrorCode error) {
@@ -47,14 +47,14 @@ private static <T extends Enum<T>> void reportError(String vendor, T error, T ok
4747
if(error == null || error == ok) {
4848
return;
4949
}
50-
System.err.println("Error: " + error.name() + " occured while configuring " + vendor + " motor");
50+
System.err.println("Error: " + error.name() + " occurred while configuring " + vendor + " motor");
5151
System.err.println("Full stack trace:");
5252
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
5353
System.err.println(Arrays.toString(stack));
5454
}
5555

5656
public static void checkSparkMaxErrors(CANSparkMax spark) {
57-
//Purposely obivously impersonal to differentiate from actual computer generated errors
57+
//Purposely obviously impersonal to differentiate from actual computer generated errors
5858
short faults = spark.getFaults();
5959
short stickyFaults = spark.getStickyFaults();
6060
short prevFaults = flags.containsKey(spark) ? flags.get(spark) : 0;
@@ -114,7 +114,7 @@ public static void doReportSparkMaxTemp() {
114114
temperatureSparks.forEach((port, spark) -> {
115115
double temp = spark.getMotorTemperature();
116116
SmartDashboard.putNumber("Port " + port + " Spark Max Temp", temp);
117-
// Check if temperature exceeds the setpoint or if the contoller has already overheated to prevent other code from resetting the current limit after the controller has cooled
117+
// Check if temperature exceeds the setpoint or if the controller has already overheated to prevent other code from resetting the current limit after the controller has cooled
118118
if(temp >= sparkTemperatureLimits.get(port) || overheatedSparks.contains(port)) {
119119
if(!overheatedSparks.contains(port)) {
120120
overheatedSparks.add(port);

src/main/java/org/carlmontrobotics/lib199/path/RobotPath.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ public Command getPathCommand(boolean faceInPathDirection, boolean stopAtEnd) {
8585
if (trajectory == null) {
8686
generateTrajectory();
8787
}
88-
hs.reset();
8988
// We want the robot to stay facing the same direction (in this case), so save
9089
// the current heading (make sure to update at the start of the command)
9190
AtomicReference<Rotation2d> headingRef = new AtomicReference<>(dt.getPose().getRotation());
9291
Supplier<Rotation2d> desiredHeading = (!faceInPathDirection) ? () -> headingRef.get() : () -> hs.sample();
9392
Command command = dt.createAutoCommand(trajectory, desiredHeading);
93+
command = new InstantCommand(hs::reset).andThen(command, new InstantCommand(hs::stop));
9494
if (stopAtEnd) {
9595
command = command.andThen(new InstantCommand(dt::stop, dt));
9696
}
@@ -310,5 +310,13 @@ public void reset() {
310310
timerStarted = false;
311311
timer.reset();
312312
}
313+
314+
/**
315+
* Stops the timer
316+
*/
317+
public void stop() {
318+
timer.stop();
319+
reset();
320+
}
313321
}
314322
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.carlmontrobotics.lib199.sim;
22

3-
import java.util.ArrayList;
4-
import java.util.HashMap;
3+
import java.util.concurrent.ConcurrentHashMap;
4+
import java.util.concurrent.CopyOnWriteArrayList;
55

66
import com.ctre.phoenix.motorcontrol.ControlMode;
77
import com.ctre.phoenix.motorcontrol.IMotorController;
@@ -15,7 +15,7 @@ abstract class MockPhoenixController implements AutoCloseable {
1515
// CAN ports should be separate from PWM ports
1616
protected PWMMotorController motorPWM;
1717
// Since we need to keep a record of all the motor's followers
18-
protected static HashMap<Integer, ArrayList<PWMMotorController>> followMap = new HashMap<>();
18+
protected static ConcurrentHashMap<Integer, CopyOnWriteArrayList<PWMMotorController>> followMap = new ConcurrentHashMap<>();
1919

2020
public MockPhoenixController(int portPWM) {
2121
this.portPWM = portPWM;
@@ -36,7 +36,7 @@ public double get() {
3636

3737
public void follow(IMotorController leader) {
3838
if (!followMap.containsKey(leader.getDeviceID())) {
39-
ArrayList<PWMMotorController> arr = new ArrayList<PWMMotorController>();
39+
CopyOnWriteArrayList<PWMMotorController> arr = new CopyOnWriteArrayList<PWMMotorController>();
4040
arr.add(motorPWM);
4141
followMap.put(leader.getDeviceID(), arr);
4242
} else {

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
package org.carlmontrobotics.lib199.sim;
22

3-
import java.util.ArrayList;
4-
import java.util.HashMap;
3+
import java.util.concurrent.ConcurrentHashMap;
4+
import java.util.concurrent.CopyOnWriteArrayList;
5+
6+
import org.carlmontrobotics.lib199.DummySparkMaxAnswer;
7+
import org.carlmontrobotics.lib199.Mocks;
8+
import org.carlmontrobotics.lib199.REVLibErrorAnswer;
59

6-
import com.revrobotics.REVLibError;
7-
import com.revrobotics.RelativeEncoder;
8-
import com.revrobotics.SparkMaxPIDController;
910
import com.revrobotics.CANSparkMax;
1011
import com.revrobotics.CANSparkMax.ExternalFollower;
1112
import com.revrobotics.CANSparkMax.IdleMode;
1213
import com.revrobotics.CANSparkMaxLowLevel.MotorType;
13-
14-
import org.carlmontrobotics.lib199.DummySparkMaxAnswer;
15-
import org.carlmontrobotics.lib199.Mocks;
16-
import org.carlmontrobotics.lib199.REVLibErrorAnswer;
14+
import com.revrobotics.REVLibError;
15+
import com.revrobotics.RelativeEncoder;
16+
import com.revrobotics.SparkMaxPIDController;
1717

1818
import edu.wpi.first.hal.SimDevice;
19-
import edu.wpi.first.hal.SimDouble;
2019
import edu.wpi.first.hal.SimDevice.Direction;
20+
import edu.wpi.first.hal.SimDouble;
2121

2222
public class MockSparkMax {
2323
// Assign the CAN port to a PWM port so it works with the simulator. Not a fan
@@ -30,7 +30,7 @@ public class MockSparkMax {
3030
private SparkMaxPIDController pidController;
3131
private boolean isInverted;
3232
// Since we need to keep a record of all the motor's followers
33-
private static HashMap<Integer, ArrayList<SimDouble>> followMap = new HashMap<>();
33+
private static ConcurrentHashMap<Integer, CopyOnWriteArrayList<SimDouble>> followMap = new ConcurrentHashMap<>();
3434

3535
public MockSparkMax(int port, MotorType type) {
3636
this.port = port;
@@ -67,7 +67,7 @@ public REVLibError follow(ExternalFollower leader, int deviceID) {
6767

6868
public REVLibError follow(ExternalFollower leader, int deviceID, boolean invert) {
6969
if (!followMap.containsKey(deviceID)) {
70-
followMap.put(deviceID, new ArrayList<SimDouble>());
70+
followMap.put(deviceID, new CopyOnWriteArrayList<SimDouble>());
7171
}
7272
followMap.get(deviceID).add(speed);
7373
return REVLibError.kOk;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public MockedCANCoder(CANCoder canCoder) {
2929
position = device.createDouble("count", Direction.kInput, 0);
3030
gearing = device.createDouble("gearing", Direction.kOutput, 1);
3131
sim = canCoder.getSimCollection();
32-
Lib199Subsystem.registerPeriodic(this::update);
32+
Lib199Subsystem.registerAsyncSimulationPeriodic(this::update);
3333
sims.put(port, this);
3434
}
3535

0 commit comments

Comments
 (0)