Code structuring question (Dependency Injection vs .Singleton...)

Hey Everyone,

We are in the process of coding an LED subsystem (Candle) and we’re curious about the optimal way to structure the code. Does it make sense to:

  1. Leverage Dependency Injection and have our Intake/Vision System take in the LED system. Maybe through overloading the constructor (i.e., Intake(LEDSubsystem lights) as an overloaded constructor). This would give us access to the LED subsystem’s methods in the intake and vision subsystem and then we could periodically poll to see when we should apply a specific color changing method. Or maybe thinking about the inverse: the LED Subsystem’s constructor could parameterize BooleanSuppliers that reference the isGamePieceIndexed and is VisionAccepted. So, in the LED Subsystems Periodic we can constantly check those Boolean states and adjust the color as needed.

  2. Global LED Subsystem or Singleton. By having the LED as a singleton we could access it as needed globally. Not sure if this is best practice, but this would let us periodically call the LED methods as needed from the other subsystems’ periodics.

  3. Tie it into the Command Scheduler. Do we keep the LED Subsystem and the others separate and have them only interact through Commands/Triggers. So, in Robot Container when a trigger is true, run commands from the LED Subsystem. Which reminds me, does a trigger using a certain subsystem Boolean-state interfere with other commands using/requiring the subsystem? So, if I want to assign the isPieceIndexed to a trigger that runs the LEDSetColorCommand, does that mess with another command running that requires the Intake subsystem?

I realize that there is more than one way to skin a cat, but I’m just curious about this from a design/software lens. I want to make sure we’re practicing the best practices, coding consistency, and learning from this.

I appreciate all input!

I would recommend (3), having the intake subsystem publish a trigger that you bind to an LED command in RobotContainer. The event loop will then schedule the command for you. The LED command should come from a command factory on the LED subsystem.

m_intake.isPieceIndexed.whileTrue(m_leds.flashGreen());

Triggers are not commands and do not have requirements on subsystems. If you were to create a command that merely reads the state of a subsystem, I would recommend not requiring that subsystem.

I wrote an article on Best Practices for Command-Based Programming.

4 Likes

That makes a lot of sense!

I felt that the more you inject between subsystems the more data there is floating around. It starts to get very messy. I like the idea of keeping everything running along the command scheduler.

m_intake.isPieceIndexed.whileTrue(m_leds.flashGreen());

This code snippet helped a lot!

2 Likes