2020-12-26 14:12:05 -08:00
|
|
|
// 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.
|
2019-08-25 23:55:59 -04:00
|
|
|
|
|
|
|
|
#include "frc2/command/button/Trigger.h"
|
|
|
|
|
|
2024-09-20 17:43:39 -07:00
|
|
|
#include <utility>
|
|
|
|
|
|
2021-12-28 12:49:41 -05:00
|
|
|
#include <frc/filter/Debouncer.h>
|
[wpimath,cmd] Add multi tap boolean stream filter and multi tap trigger modifier (double tap detector) (#8307)
Add a simple tap counting filter for boolean streams.
The filter activates when the input has risen (transitioned from false
to true, like when a button is tapped) the required number of times
within the time window after the first rising edge. Once activated, the
output remains true as long as the input is true. The tap count resets
when the time window expires or when the input goes false after
activation.
Example usage:
```java
xbox.a()
.multiPress(2, 0.2) // Detect a double tap within 0.2 seconds
.onTrue(Commands.print("Double tapped A button"));
xbox.y()
.multiPress(2, 0.5) // Detect a double tap within 0.5 seconds
.whileTrue(Commands.print("Y held after tap").repeatedly());
```
This is not a noise reduction and/or input smoothing filter, but it is
similar in usage to debounce, so I believe it could be considered a
filter, but am open to a better location.
I believe this would be a useful addition, as double/triple tapping a
button is a common control option in games, yet is not often utilized by
newer FRC teams. I believe adding it to WPILib in a standard way will
allow more teams to make the most out of their controls.
2026-01-14 20:22:07 -08:00
|
|
|
#include <frc/filter/EdgeCountFilter.h>
|
2021-09-19 19:58:16 -07:00
|
|
|
|
2024-09-20 17:43:39 -07:00
|
|
|
#include "frc2/command/CommandPtr.h"
|
2019-08-25 23:55:59 -04:00
|
|
|
|
2022-06-09 08:16:51 +03:00
|
|
|
using namespace frc;
|
2019-08-25 23:55:59 -04:00
|
|
|
using namespace frc2;
|
|
|
|
|
|
2020-12-28 00:37:33 -08:00
|
|
|
Trigger::Trigger(const Trigger& other) = default;
|
2019-08-25 23:55:59 -04:00
|
|
|
|
2024-12-14 23:13:41 -08:00
|
|
|
void Trigger::AddBinding(wpi::unique_function<void(bool, bool)>&& body) {
|
|
|
|
|
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
|
|
|
|
body = std::move(body)]() mutable {
|
|
|
|
|
bool current = condition();
|
2024-04-22 00:37:15 -05:00
|
|
|
|
2024-12-14 23:13:41 -08:00
|
|
|
body(previous, current);
|
|
|
|
|
|
|
|
|
|
previous = current;
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-04-22 00:37:15 -05:00
|
|
|
|
2024-12-14 23:13:41 -08:00
|
|
|
Trigger Trigger::OnChange(Command* command) {
|
|
|
|
|
AddBinding([command](bool previous, bool current) {
|
|
|
|
|
if (previous != current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2024-12-14 23:13:41 -08:00
|
|
|
}
|
|
|
|
|
});
|
2024-04-22 00:37:15 -05:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::OnChange(CommandPtr&& command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command = std::move(command)](bool previous, bool current) {
|
2024-04-22 00:37:15 -05:00
|
|
|
if (previous != current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2024-04-22 00:37:15 -05:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
Trigger Trigger::OnTrue(Command* command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command](bool previous, bool current) {
|
|
|
|
|
if (!previous && current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2024-12-14 23:13:41 -08:00
|
|
|
}
|
|
|
|
|
});
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::OnTrue(CommandPtr&& command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command = std::move(command)](bool previous, bool current) {
|
2022-11-28 23:48:48 +02:00
|
|
|
if (!previous && current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2022-11-28 23:48:48 +02:00
|
|
|
}
|
|
|
|
|
});
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::OnFalse(Command* command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command](bool previous, bool current) {
|
|
|
|
|
if (previous && !current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2024-12-14 23:13:41 -08:00
|
|
|
}
|
|
|
|
|
});
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::OnFalse(CommandPtr&& command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command = std::move(command)](bool previous, bool current) {
|
2022-11-28 23:48:48 +02:00
|
|
|
if (previous && !current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2022-11-28 23:48:48 +02:00
|
|
|
}
|
|
|
|
|
});
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::WhileTrue(Command* command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command](bool previous, bool current) {
|
|
|
|
|
if (!previous && current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2024-12-14 23:13:41 -08:00
|
|
|
} else if (previous && !current) {
|
|
|
|
|
command->Cancel();
|
|
|
|
|
}
|
|
|
|
|
});
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::WhileTrue(CommandPtr&& command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command = std::move(command)](bool previous, bool current) {
|
2022-11-28 23:48:48 +02:00
|
|
|
if (!previous && current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2022-11-28 23:48:48 +02:00
|
|
|
} else if (previous && !current) {
|
|
|
|
|
command.Cancel();
|
|
|
|
|
}
|
|
|
|
|
});
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::WhileFalse(Command* command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command](bool previous, bool current) {
|
|
|
|
|
if (previous && !current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2024-12-14 23:13:41 -08:00
|
|
|
} else if (!previous && current) {
|
|
|
|
|
command->Cancel();
|
|
|
|
|
}
|
|
|
|
|
});
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::WhileFalse(CommandPtr&& command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command = std::move(command)](bool previous, bool current) {
|
2022-11-28 23:48:48 +02:00
|
|
|
if (!previous && current) {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2022-11-28 23:48:48 +02:00
|
|
|
} else if (previous && !current) {
|
|
|
|
|
command.Cancel();
|
|
|
|
|
}
|
|
|
|
|
});
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::ToggleOnTrue(Command* command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command](bool previous, bool current) {
|
2022-11-28 23:48:48 +02:00
|
|
|
if (!previous && current) {
|
|
|
|
|
if (command->IsScheduled()) {
|
|
|
|
|
command->Cancel();
|
|
|
|
|
} else {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2022-11-28 23:48:48 +02:00
|
|
|
}
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::ToggleOnTrue(CommandPtr&& command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command = std::move(command)](bool previous, bool current) {
|
2022-11-28 23:48:48 +02:00
|
|
|
if (!previous && current) {
|
|
|
|
|
if (command.IsScheduled()) {
|
|
|
|
|
command.Cancel();
|
|
|
|
|
} else {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2022-11-28 23:48:48 +02:00
|
|
|
}
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Trigger Trigger::ToggleOnFalse(Command* command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command](bool previous, bool current) {
|
2022-11-28 23:48:48 +02:00
|
|
|
if (previous && !current) {
|
|
|
|
|
if (command->IsScheduled()) {
|
|
|
|
|
command->Cancel();
|
|
|
|
|
} else {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2022-11-28 23:48:48 +02:00
|
|
|
}
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
}
|
|
|
|
|
});
|
2019-08-25 23:55:59 -04:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
Trigger Trigger::ToggleOnFalse(CommandPtr&& command) {
|
2024-12-14 23:13:41 -08:00
|
|
|
AddBinding([command = std::move(command)](bool previous, bool current) {
|
2022-11-28 23:48:48 +02:00
|
|
|
if (previous && !current) {
|
|
|
|
|
if (command.IsScheduled()) {
|
|
|
|
|
command.Cancel();
|
|
|
|
|
} else {
|
2025-07-02 16:46:59 -04:00
|
|
|
frc2::CommandScheduler::GetInstance().Schedule(command);
|
2022-11-28 23:48:48 +02:00
|
|
|
}
|
[command] Rename trigger methods (#4210)
Motivation
Feedback from 2022 showed that the Trigger API is rather confusing, mostly due to the following:
- duplicate Trigger and Button APIs were available; users were confused searching for a nonexistent difference between them.
- the when terminology was ambiguous and unclear whether it refers to the high state or specifically the rising edge.
- the Active terminology didn't unambiguously refer to the high state; it wasn't unintuitive to understand it as "when the binding is active/polled".
- whileHeld vs whenHeld was very confusing, and the difference between them wasn't obvious. The parallel Trigger verbs, whileActiveContinuously and whileActiveOnce are much less confusing.
Solution
Deprecating Button and its binding methods. The rationale for deprecating Button (and not Trigger) is because Button uses terminology that is needlessly more specific and restricting to the button use case, making the use case of arbitrary trigger conditions unintuitive.
After consideration, deprecation of Button's subclasses was decided against:
- NetworkButton (a trigger condition based on a boolean NT entry/topic) is a use case that is not necessarily intuitive for teams to implement themselves, so it is an abstraction that should be provided in the library. A parallel class for the BooleanEvent level, NetworkBooleanEvent, was also added as part of NT4. NT listeners were considered as a alternative solution, but they require attention to thread safety, and aren't interoperable with the EventLoop API.
- JoystickButton/POVButton provide abstractions around HID buttons. The new Trigger-returning factories on the HID classes are an equal (if not more concise) alternative, but there is no reason not to keep them for those who find their use preferable.
At a later date in the deprecation cycle (perhaps for 2024), when Button is removed, these subclasses should be changed to inherit directly from Trigger.
Trigger's bindings are changed to use True/False terminology, as it should be unambiguous. Each binding type has both True and False variants; for brevity, only the True variants are listed here:
- onTrue (replaces whenActive): schedule on rising edge.
- whileTrue (replaces whileActiveOnce): schedule on rising edge, cancel on falling edge.
- toggleOnTrue (replaces toggleWhenActive): on rising edge, schedule if unscheduled and cancel if scheduled.
Two binding types are completely deprecated:
- cancelWhenActive: this is a fairly niche use case which is better described as having the trigger's rising edge (Trigger.rising()) as an end condition for the command (using Command.until()).
- whileActiveContinuously: however common, this relied on the no-op behavior of scheduling an already-scheduled command. The more correct way to repeat the command if it ends before the falling edge is using Command.repeatedly/RepeatCommand or a RunCommand -- the only difference is if the command is interrupted, but that is more likely to result in two commands perpetually canceling each other than achieve the desired behavior. Manually implementing a blindly-scheduling binding like whileActiveContinuously is still possible, though might not be intuitive.
Notes
It was considered to share BooleanEvent's digital signal terminology; however, once it was decided that Trigger should not inherit from BooleanEvent (due to overload incompatibility) the common terminology was not worth the unintuitiveness stemming from users' unfamiliarity with the signal processing terms.
2022-10-28 08:03:28 +03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-28 23:48:48 +02:00
|
|
|
Trigger Trigger::Debounce(units::second_t debounceTime,
|
|
|
|
|
frc::Debouncer::DebounceType type) {
|
|
|
|
|
return Trigger(m_loop, [debouncer = frc::Debouncer(debounceTime, type),
|
|
|
|
|
condition = m_condition]() mutable {
|
|
|
|
|
return debouncer.Calculate(condition());
|
|
|
|
|
});
|
2019-08-25 23:55:59 -04:00
|
|
|
}
|
2023-11-14 17:54:43 -06:00
|
|
|
|
[wpimath,cmd] Add multi tap boolean stream filter and multi tap trigger modifier (double tap detector) (#8307)
Add a simple tap counting filter for boolean streams.
The filter activates when the input has risen (transitioned from false
to true, like when a button is tapped) the required number of times
within the time window after the first rising edge. Once activated, the
output remains true as long as the input is true. The tap count resets
when the time window expires or when the input goes false after
activation.
Example usage:
```java
xbox.a()
.multiPress(2, 0.2) // Detect a double tap within 0.2 seconds
.onTrue(Commands.print("Double tapped A button"));
xbox.y()
.multiPress(2, 0.5) // Detect a double tap within 0.5 seconds
.whileTrue(Commands.print("Y held after tap").repeatedly());
```
This is not a noise reduction and/or input smoothing filter, but it is
similar in usage to debounce, so I believe it could be considered a
filter, but am open to a better location.
I believe this would be a useful addition, as double/triple tapping a
button is a common control option in games, yet is not often utilized by
newer FRC teams. I believe adding it to WPILib in a standard way will
allow more teams to make the most out of their controls.
2026-01-14 20:22:07 -08:00
|
|
|
Trigger Trigger::MultiPress(int requiredPresses, units::second_t windowTime) {
|
|
|
|
|
return Trigger(m_loop,
|
|
|
|
|
[filter = frc::EdgeCounterFilter(requiredPresses, windowTime),
|
|
|
|
|
condition = m_condition]() mutable {
|
|
|
|
|
return filter.Calculate(condition());
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 17:54:43 -06:00
|
|
|
bool Trigger::Get() const {
|
|
|
|
|
return m_condition();
|
|
|
|
|
}
|