-
Notifications
You must be signed in to change notification settings - Fork 611
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
Refactor TimedRobot, add support for one-shot actions and cancelling … #7315
base: main
Are you sure you want to change the base?
Conversation
…actions, along with support for actions that cancel themselves. Changes include: Implement TimedRobot.Cancellable, a functional interface invoked to cancel a scheduled callback. Implement an enum represention of the schedule status where each enumeration contains a callback handler. Implement TimedRobot.Callback.invoke() to handle Runnable invocation on behalf of the TimedRobot, where the method returns true if and only if the callback should be invoked again. Have invoke() delegate callback invocation to the callback's status. Replace Callback.func.run() invocations with Callback.invoke(), and reschedule the callback if and only if invoke() returns true. Enhance TimedRobot.Callback to maintain its schedule status and enhance the addPeriodic() methods to set the scheduling status to PERIODIC (i.e. indefinite periodic invocation) and to return a Cancellable (the Callback that holds the user-provided Runnable) so that applications can cancel invocation at will. Base the enhanced class on Cancellable and implement cancel(). The returned Cancellable is actually the Callable that holds the user's Runnable. Add TimedRobot.addOneShot(), which schedules a Runnable to be invoked exactly once after a set delay. Like its addPeriodic() counterparts, addOneShot() returns a Cancellable. The enhanced TimedRobot currently provides one addOneShot version that takes a timeout in seconds. Please let me know if users will need to specify delays as a Time. Simplify the loop in TimedRobot.startCompetition() and enhance it to reschedule a callback when and only when its callback() invocation returns true. Note that the status enumerations, PERIODIC, ONE_SHOT, and CANCELLED each provide an invoke() method that accepts a Runnable and "does the right thing." The enumerations have the following characteristics | Name | Action | Runs Callback | Returns | +-----------+---------------------- +---------------+---------+ | PERIODIC | Invoke indefinitately | Yes | true | +-----------+---------------------- +---------------+---------+ | ONE_SHOT | Invoke exactly once | Yes | false | +-----------+---------------------- +---------------+---------+ | CANCELLED | Cancel invocation | No | false | +-----------+---------------------- +---------------+---------+ For example, ONE_SHOT.invoke(Runnable callback) invokes callback.run(), but returns false so it will not be rescheduled. The other implications work similarly.
What's the intended use case? (What's a specific situation in robot code where this would be useful?) |
One-shot is useful for detecting failures via exceeded deadlines. For example, failure to complete an action by its deadline could indicate a mechanical failure like a belt breakage or chain coming off. The fired action would stop the motor to limit knock-on damage and assure safety. It can also be used for automation, especially by low resourced teams that cannot afford sophisticated motors and controllers, e.g. teams limited to PWM. The software could start a motor and enqueue a one-shot action to stop it after a specific time. It might be useful for, say, indexing or auto navigation.
Event cancellation would be useful in setting a motor startup profile. It's sometimes useful to spin a motor up (relitively) slowly to reduce stress on robot components. The action would increase motor power (e.g.) at each step and cancel itself when it reaches the target power. Another use would be to manage LED effects in individually addressable RGB LED strips or panels composed of WS2812B LEDs or similar. An action could cycle through an effect and cancel itself when the effect finishes. Well resourced teams can use co-processors (we use an ESP32s) but teams who can't afford this or lack the required skills to use them could implement effects this way. Note that this applies to less sophisticated LED strips, too.
Hope that this helps.
…On 10/30/24 10:38 PM, Joseph Eng ***@***.***> wrote:
What's the intended use case? (What's a specific situation in robot code
where this would be useful?)
—
Reply to this email directly, view it on GitHub <https://github.com/
wpilibsuite/allwpilib#7315#issuecomment-2448903749>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/
AD6QNG5VTDEOJ2TL24OD4K3Z6GJ2HAVCNFSM6AAAAABQ5APRZCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINBYHEYDGNZUHE>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
…actions, along with support for actions that cancel themselves. Changes include: Implement TimedRobot.Cancellable, a functional interface invoked to cancel a scheduled callback. Implement an enum represention of the schedule status where each enumeration contains a callback handler. Implement TimedRobot.Callback.invoke() to handle Runnable invocation on behalf of the TimedRobot, where the method returns true if and only if the callback should be invoked again. Have invoke() delegate callback invocation to the callback's status. Replace Callback.func.run() invocations with Callback.invoke(), and reschedule the callback if and only if invoke() returns true. Enhance TimedRobot.Callback to maintain its schedule status and enhance the addPeriodic() methods to set the scheduling status to PERIODIC (i.e. indefinite periodic invocation) and to return a Cancellable (the Callback that holds the user-provided Runnable) so that applications can cancel invocation at will. Base the enhanced class on Cancellable and implement cancel(). The returned Cancellable is actually the Callable that holds the user's Runnable. Add TimedRobot.addOneShot(), which schedules a Runnable to be invoked exactly once after a set delay. Like its addPeriodic() counterparts, addOneShot() returns a Cancellable. The enhanced TimedRobot currently provides one addOneShot version that takes a timeout in seconds. Please let me know if users will need to specify delays as a Time. Simplify the loop in TimedRobot.startCompetition() and enhance it to reschedule a callback when and only when its callback() invocation returns true. Note that the status enumerations, PERIODIC, ONE_SHOT, and CANCELLED each provide an invoke() method that accepts a Runnable and "does the right thing." The enumerations have the following characteristics | Name | Action | Runs Callback | Returns | +-----------+---------------------- +---------------+---------+ | PERIODIC | Invoke indefinitately | Yes | true | +-----------+---------------------- +---------------+---------+ | ONE_SHOT | Invoke exactly once | Yes | false | +-----------+---------------------- +---------------+---------+ | CANCELLED | Cancel invocation | No | false | +-----------+---------------------- +---------------+---------+ For example, ONE_SHOT.invoke(Runnable callback) invokes callback.run(), but returns false so it will not be rescheduled. The other implications work similarly. Also fix lint errors.
Just fixed the lint errors in the enhancement and created a pull request
with the fixes. I hope that I was correct and proper.
Thanks for helping me ramp up. I'm still learning Git.
…On 10/31/24 14:42, ***@***.*** wrote:
One-shot is useful for detecting failures via exceeded deadlines. For
example, failure to complete an action by its deadline could indicate
a mechanical failure like a belt breakage or chain coming off. The
fired action would stop the motor to limit knock-on damage and assure
safety. It can also be used for automation, especially by low
resourced teams that cannot afford sophisticated motors and
controllers, e.g. teams limited to PWM. The software could start a
motor and enqueue a one-shot action to stop it after a specific time.
It might be useful for, say, indexing or auto navigation.
Event cancellation would be useful in setting a motor startup profile.
It's sometimes useful to spin a motor up (relitively) slowly to reduce
stress on robot components. The action would increase motor power
(e.g.) at each step and cancel itself when it reaches the target
power. Another use would be to manage LED effects in individually
addressable RGB LED strips or panels composed of WS2812B LEDs or
similar. An action could cycle through an effect and cancel itself
when the effect finishes. Well resourced teams can use co-processors
(we use an ESP32s) but teams who can't afford this or lack the
required skills to use them could implement effects this way. Note
that this applies to less sophisticated LED strips, too.
Hope that this helps.
On 10/30/24 10:38 PM, Joseph Eng ***@***.***> wrote:
> What's the intended use case? (What's a specific situation in robot
> code where this would be useful?)
>
> —
> Reply to this email directly, view it on GitHub <https://github.com/
> wpilibsuite/allwpilib#7315#issuecomment-2448903749>, or
> unsubscribe <https://github.com/notifications/unsubscribe-auth/
> AD6QNG5VTDEOJ2TL24OD4K3Z6GJ2HAVCNFSM6AAAAABQ5APRZCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINBYHEYDGNZUHE>.
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>
>
|
Thanks for the explanation! Those are great examples. However, those can all be implemented via the command based framework. (For example, to stop a motor after 1.5 seconds, use |
Fair point. I've just started to mentor the programming task force and clearly have a lot to learn. My first role was mentoring the electrical task force, which I still do.
However, there's still equipment protection. Suppose the robot has an elevator controlled by limit switches that is supposed to raise in, say, 1.5 seconds. I can schedule a protective action that turns off the motor to run in two seconds and cancel it when the upper limit switch closes. When the operation succeeds, the protective action does not run. When the operation fails and the limit switch doesn't close soon enough, the action runs and stops the elevator motor. Is there already a way to accomplish this?
How do you see the self cancelling periodic actions? Do the examples make sense?
Incidentally, please let me know if I can be working on a higher priority issue. I'd be happy to set this aside.
All my best,
Eric
…On 10/31/24 4:19 PM, Joseph Eng ***@***.***> wrote:
Thanks for the explanation! Those are great examples. However, those can
all be implemented via the command based framework. (For example, to
stop a motor after 1.5 seconds, use |
runMotor().withTimeout(1.5).andThen(stopMotor())| where |runMotor()| and
|stopMotor()| return commands) Any reason to add another way to do these
sorts of tasks?
—
Reply to this email directly, view it on GitHub <https://github.com/
wpilibsuite/allwpilib#7315#issuecomment-2450742288>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/
AD6QNGZPHGQFRAJ6MNAQ7PTZ6KGFBAVCNFSM6AAAAABQ5APRZCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINJQG42DEMRYHA>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Usually, you can always run the protective action after the command is done, whether or not it succeeded or was canceled. In that case, you would just run the protective action inside There are a few exceptions that don't fit that category, but they're a bit harder to explain so I won't do so now. If you do have a case like that, though, feel free to send a link to the code or describe it and we can discuss other solutions!
I think the examples show some great use cases, but the commands library was made for those very cases, so I don't think it's worth the complexity (which includes more stuff to update when we make changes).
Thanks for contributing! I don't know if there are higher priority issues that would be good for you to start on (Full disclosure- I'm not one of the main developers, I just make some contributions when I have free time), but issues labeled "good first issue" are a good place to get started and then you can see what sort of stuff is going on and see what you'd like to work on. |
…actions, along with support for actions that cancel themselves. Changes include:
Implement TimedRobot.Cancellable, a functional interface invoked to cancel a scheduled callback.
Implement an enum represention of the schedule status where each enumeration contains a callback handler.
Implement TimedRobot.Callback.invoke() to handle Runnable invocation on behalf of the TimedRobot, where the method returns true if and only if the callback should be invoked again. Have invoke() delegate callback invocation to the callback's status. Replace Callback.func.run() invocations with Callback.invoke(), and reschedule the callback if and only if invoke() returns true.
Enhance TimedRobot.Callback to maintain its schedule status and enhance the addPeriodic() methods to set the scheduling status to PERIODIC (i.e. indefinite periodic invocation) and to return a Cancellable (the Callback that holds the user-provided Runnable) so that applications can cancel invocation at will. Base the enhanced class on Cancellable and implement cancel(). The returned Cancellable is actually the Callable that holds the user's Runnable.
Add TimedRobot.addOneShot(), which schedules a Runnable to be invoked exactly once after a set delay. Like its addPeriodic() counterparts, addOneShot() returns a Cancellable. The enhanced TimedRobot currently provides one addOneShot version that takes a timeout in seconds. Please let me know if users will need to specify delays as a Time.
Simplify the loop in TimedRobot.startCompetition() and enhance it to reschedule a callback when and only when its callback() invocation returns true.
Note that the status enumerations, PERIODIC, ONE_SHOT, and CANCELLED each provide an invoke() method that accepts a Runnable and "does the right thing." The enumerations have the following characteristics
| Name | Action | Runs Callback | Returns |
+-----------+---------------------- +---------------+---------+
| PERIODIC | Invoke indefinitately | Yes | true |
+-----------+---------------------- +---------------+---------+
| ONE_SHOT | Invoke exactly once | Yes | false |
+-----------+---------------------- +---------------+---------+
| CANCELLED | Cancel invocation | No | false |
+-----------+---------------------- +---------------+---------+
For example, ONE_SHOT.invoke(Runnable callback) invokes callback.run(), but returns false so it will not be rescheduled. The other implications work similarly.