-
Notifications
You must be signed in to change notification settings - Fork 668
[commands v3] Make Mechanism an interface #8303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 2027
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Daniel1464 marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // Copyright (c) FIRST and other WPILib contributors. | ||
| // Open Source Software; you can modify and/or share it under the terms of | ||
| // the WPILib BSD license file in the root directory of this project. | ||
|
|
||
| package org.wpilib.commands3; | ||
|
|
||
| /** A dummy mechanism that allows inline scheduler and name specification, for use in unit tests. */ | ||
| class DummyMechanism implements Mechanism { | ||
| private final String m_name; | ||
| private final Scheduler m_scheduler; | ||
|
|
||
| /** | ||
| * Creates a dummy mechanism. | ||
| * | ||
| * @param name The name of this dummy mechanism. | ||
| * @param scheduler The registered scheduler. Cannot be null. | ||
| */ | ||
| DummyMechanism(String name, Scheduler scheduler) { | ||
| m_name = name; | ||
| m_scheduler = scheduler; | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return m_name; | ||
| } | ||
|
|
||
| @Override | ||
| public Scheduler getRegisteredScheduler() { | ||
| return m_scheduler; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,7 @@ class SchedulerCancellationTests extends CommandTestBase { | |
| void cancelOnInterruptDoesNotResume() { | ||
| var count = new AtomicInteger(0); | ||
|
|
||
| var mechanism = new Mechanism("mechanism", m_scheduler); | ||
| var mechanism = new DummyMechanism("mechanism", m_scheduler); | ||
|
|
||
| var interrupter = | ||
| Command.requiring(mechanism) | ||
|
|
@@ -54,7 +54,7 @@ void cancelOnInterruptDoesNotResume() { | |
| void defaultCommandResumesAfterInterruption() { | ||
| var count = new AtomicInteger(0); | ||
|
|
||
| var mechanism = new Mechanism("mechanism", m_scheduler); | ||
| var mechanism = new DummyMechanism("mechanism", m_scheduler); | ||
| var defaultCmd = | ||
| mechanism | ||
| .run( | ||
|
|
@@ -169,7 +169,9 @@ void cancelAllDoesNotCallOnCancelHookForQueuedCommands() { | |
| void cancelAllStartsDefaults() { | ||
| var mechanisms = new ArrayList<Mechanism>(10); | ||
| for (int i = 1; i <= 10; i++) { | ||
| mechanisms.add(new Mechanism("System " + i, m_scheduler)); | ||
| var mechanism = new DummyMechanism("System " + i, m_scheduler); | ||
| mechanism.setDefaultCommand(mechanism.idle()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not put a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the default command of Mechanism is now null instead of idle(), and since DummyMechanism is just supposed to be a Mechanism itself, I don't think having it implicitly do this is the best. I could be wrong tho
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 Yeah, that seems fair. I guess another option would be to have a decorator that sets the default command: public DummyMechanism withIdleDefaultCommand() {
setDefaultCommand(idle());
return this;
}
// Then we can do e.g. mechanisms.add(new DummyMechanism("System " + i, m_scheduler).withIdleDefaultCommand());This is just for illustrative purposes- Exact signature (return type and name) can vary. |
||
| mechanisms.add(mechanism); | ||
| } | ||
|
|
||
| var command = Command.requiring(mechanisms).executing(Coroutine::yield).named("Big Command"); | ||
|
|
@@ -237,7 +239,7 @@ void cancelDeeplyNestedCompositions() { | |
|
|
||
| @Test | ||
| void compositionsDoNotSelfCancel() { | ||
| var mech = new Mechanism("The mechanism", m_scheduler); | ||
| var mech = new DummyMechanism("The mechanism", m_scheduler); | ||
| var group = | ||
| mech.run( | ||
| co -> { | ||
|
|
@@ -263,7 +265,7 @@ void compositionsDoNotSelfCancel() { | |
|
|
||
| @Test | ||
| void compositionsDoNotCancelParent() { | ||
| var mech = new Mechanism("The mechanism", m_scheduler); | ||
| var mech = new DummyMechanism("The mechanism", m_scheduler); | ||
| var group = | ||
| mech.run( | ||
| co -> { | ||
|
|
@@ -286,7 +288,7 @@ void compositionsDoNotCancelParent() { | |
| void doesNotRunOnCancelWhenInterruptingOnDeck() { | ||
| var ran = new AtomicBoolean(false); | ||
|
|
||
| var mechanism = new Mechanism("The mechanism", m_scheduler); | ||
| var mechanism = new DummyMechanism("The mechanism", m_scheduler); | ||
| var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd"); | ||
| var interrupter = mechanism.run(Coroutine::yield).named("Interrupter"); | ||
| m_scheduler.schedule(cmd); | ||
|
|
@@ -300,7 +302,7 @@ void doesNotRunOnCancelWhenInterruptingOnDeck() { | |
| void doesNotRunOnCancelWhenCancelingOnDeck() { | ||
| var ran = new AtomicBoolean(false); | ||
|
|
||
| var mechanism = new Mechanism("The mechanism", m_scheduler); | ||
| var mechanism = new DummyMechanism("The mechanism", m_scheduler); | ||
| var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd"); | ||
| m_scheduler.schedule(cmd); | ||
| // canceling before calling .run() | ||
|
|
@@ -314,7 +316,7 @@ void doesNotRunOnCancelWhenCancelingOnDeck() { | |
| void runsOnCancelWhenInterruptingCommand() { | ||
| var ran = new AtomicBoolean(false); | ||
|
|
||
| var mechanism = new Mechanism("The mechanism", m_scheduler); | ||
| var mechanism = new DummyMechanism("The mechanism", m_scheduler); | ||
| var cmd = mechanism.run(Coroutine::park).whenCanceled(() -> ran.set(true)).named("cmd"); | ||
| var interrupter = mechanism.run(Coroutine::park).named("Interrupter"); | ||
| m_scheduler.schedule(cmd); | ||
|
|
@@ -329,7 +331,7 @@ void runsOnCancelWhenInterruptingCommand() { | |
| void doesNotRunOnCancelWhenCompleting() { | ||
| var ran = new AtomicBoolean(false); | ||
|
|
||
| var mechanism = new Mechanism("The mechanism", m_scheduler); | ||
| var mechanism = new DummyMechanism("The mechanism", m_scheduler); | ||
| var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd"); | ||
| m_scheduler.schedule(cmd); | ||
| m_scheduler.run(); | ||
|
|
@@ -343,7 +345,7 @@ void doesNotRunOnCancelWhenCompleting() { | |
| void runsOnCancelWhenCanceling() { | ||
| var ran = new AtomicBoolean(false); | ||
|
|
||
| var mechanism = new Mechanism("The mechanism", m_scheduler); | ||
| var mechanism = new DummyMechanism("The mechanism", m_scheduler); | ||
| var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd"); | ||
| m_scheduler.schedule(cmd); | ||
| m_scheduler.run(); | ||
|
|
@@ -356,7 +358,7 @@ void runsOnCancelWhenCanceling() { | |
| void runsOnCancelWhenCancelingParent() { | ||
| var ran = new AtomicBoolean(false); | ||
|
|
||
| var mechanism = new Mechanism("The mechanism", m_scheduler); | ||
| var mechanism = new DummyMechanism("The mechanism", m_scheduler); | ||
| var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd"); | ||
|
|
||
| var group = new SequentialGroup("Seq", Collections.singletonList(cmd)); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any concerns about losing this default
toString()? I'm not familiar enough with commands v3 to know if people may print aMechanismor a collection ofMechanisms. (e.g., someone printscmd.requirements()when debugging)There also isn't much of a difference because the default
toString()also contains the class's name, but the@xxxxxxmight be confusing.It's not a huge deal, but I just want to double check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had initially (when the idea to convert first came up) suggested a split to Mechanism (interface) and MechanismBase a la v2 for this reason, though I don't feel super strongly about it (and in fact don't really like "MechanismBase" as a name).
In practice I don't think many teams give their subsystems names different from their classes, but it could show up in the case of having multiple copies of a subsystem on the robot (which I actually have done before!). If it is deemed an uncommon case it is probably fine to just tell teams to manually override it in their subsystem if they want to change the string representation (maybe include that in the Mechanism template used in the project generator?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that teams wanting subsystems to have names other than the class names can override
getName()(even with the slight amount of plumbing to have separate names for difference instances).My main concern is about the default
toString()including the@xxxxxxstuff at the end, which could be confusing for rookies. Again, it probably isn't an issue since they probably won't see thattoString()output, but I want to make sure that we have proper justification.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getName()is used in telemetry, but many inexperienced programmers will just print objects toSystem.out.printlnfor quick and dirty debugging, and that will use the defaulttoString()implementations. I don't think that's generally an issue, since most subsystems only have a single instance, and those that have multiple should overridegetName()anyway to have unique names for each instanceThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the only way people will see
toString()is from directly printing theMechanismobjects (or a collection ofMechanismobjects), then yeah it's probably fine. (Just wanted to double-check that there wasn't any other way people might see thetoString().)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, the idea was to let ppl override getName() instead.