We have been using github for a while now and it has worked well enough. Our workflow has consisted of making separate branches for different features and merging them upon completion.
However, this is prone to causing bottlenecks since we all only have one robot to test on at any one time.
I was curious what kind of workflows people here use to avoid getting hung up in situations where only one person at a time can be seriously working on the robot.
We use Peter Johnson’s first two bullet points and we merge all students’ work every day.
To have a single source code in GitHub every day there is a rule that each student has to return code to GitHub that compiles but their task doesn’t necessarily have to work because nobody else will be using it.
To make it easy for each student to select what they are working on and ignore others potentially not functioning code we have a selector-type of code that each student has they can use. The undesired systems are null. Thus the other rule is every reference to a system has to accommodate it being null with an if(xxxx == null) or not. This also has the benefit that at a match we can easily turn off a broken system (say the intake gets ripped off the robot; we are ready to disable it in software if it can’t be fixed.)
private boolean useFullRobot = false;
private boolean useScorer = false;
private boolean useBindings = false;
private boolean useExampleSubsystem = false;
private boolean useAccelerometer = false;
private boolean useGyro = false;
private boolean useDrivetrain = false;
private boolean useGrabber = false;
private boolean useWrist = false;
private boolean useArm = false;
private boolean useShoulder = false;
private boolean useGatherer = false;
private boolean useCandle = false;
private boolean useDriverController = false;
private boolean useOperatorController = false;
private boolean useMainShuffleboard = false;
private boolean useVision = false;
private boolean useUltrasonic = false;
private boolean useDataLog = false;
public final boolean fullRobot;
public final ExampleSubsystem exampleSubsystem;
public final Drivetrain drivetrain;
public final Grabber grabber;
public final Wrist wrist;
public final Arm arm;
public final Shoulder shoulder;
public final Gatherer gatherer;
public final Candle4237 candle;
public final DriverController driverController;
public final OperatorController operatorController;
public final Vision vision;
public final MainShuffleboard mainShuffleboard;
public final Accelerometer4237 accelerometer;
public final Gyro4237 gyro;
private final Ultrasonic4237 ultrasonic;
// public final PowerDistribution pdh;
public final Compressor compressor;
public DataLog log = null;
// public static final DataLog log = DataLogManager.getLog();
/**
* The container for the robot. Contains subsystems, OI devices, and commands.
* Use the default modifier so that new objects can only be constructed in the same package.
*/
RobotContainer()
{
// Create the needed subsystems
if(useFullRobot || useDataLog)
{
DataLogManager.start();
log = DataLogManager.getLog();
}
// log = (useDataLog) ? DataLogManager.getLog() : null;
fullRobot = (useFullRobot);
exampleSubsystem = (useExampleSubsystem) ? new ExampleSubsystem() : null;
accelerometer = (useAccelerometer) ? new Accelerometer4237() : null;
gyro = (useFullRobot || useGyro) ? new Gyro4237() : null;
drivetrain = (useFullRobot || useDrivetrain) ? new Drivetrain(gyro, log) : null;
grabber = (useFullRobot || useScorer || useGrabber) ? new Grabber(log) : null;
wrist = (useFullRobot || useScorer || useWrist) ? new Wrist() : null;
arm = (useFullRobot || useScorer || useArm) ? new Arm(log) : null;
shoulder = (useFullRobot || useScorer || useShoulder) ? new Shoulder(log) : null;
gatherer = (useGatherer) ? new Gatherer() : null;
candle = (useFullRobot || useCandle) ? new Candle4237() : null;
driverController = (useFullRobot || useDriverController) ? new DriverController(Constants.Controller.DRIVER) : null;
operatorController = (useFullRobot || useOperatorController) ? new OperatorController(Constants.Controller.OPERATOR) : null;
mainShuffleboard = (useFullRobot || useMainShuffleboard) ? new MainShuffleboard(this) : null;
vision = (useFullRobot || useVision) ? new Vision() : null;
compressor = (useGrabber || useWrist) ? new Compressor(0, PneumaticsModuleType.CTREPCM) : null;
ultrasonic = (useFullRobot || useUltrasonic) ? new Ultrasonic4237() : null;
We started seriously employing simulation last year, and it had a huge impact on productivity. We had a student develop on-the-fly path creation through field regions entirely in simulation. It ran perfectly on the first try on the real robot. While this was an exceptional case, it’s an example of a feature that never would have been coded in the past due to limited access to a physical robot.
As one of the students that @SLAB-Mr.Thomas is talking about , I can say that this system works well for our software team. We have been trained to come in each day and pull the most recent version of the code from GitHub, discuss with our software team and determine our tasks to ensure there is no overlap and no possibility for merge conflicts, then safely push all of our new code back to GitHub at the end of the day.
It depends on your hardware. Inspired by Mechanical Advantage’s code and wanting to leverage AdvantageKit/Scope, we created a hardware i/o abstraction layer. This allows us to create simulated versions of each subsystem. You can see this in our 3061-lib starter project. If you are fully invested in Phoenix 6, it has baked-in simulation.