2022-06-09 08:16:51 +03: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.
|
|
|
|
|
|
|
|
|
|
package edu.wpi.first.wpilibj.event;
|
|
|
|
|
|
2025-10-29 03:18:55 +00:00
|
|
|
import static edu.wpi.first.units.Units.Seconds;
|
2022-06-09 08:16:51 +03:00
|
|
|
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
|
|
|
|
|
|
|
|
|
|
import edu.wpi.first.math.filter.Debouncer;
|
2025-10-29 03:18:55 +00:00
|
|
|
import edu.wpi.first.units.measure.Time;
|
2023-10-05 00:22:57 -04:00
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
2022-06-09 08:16:51 +03:00
|
|
|
import java.util.function.BiFunction;
|
|
|
|
|
import java.util.function.BooleanSupplier;
|
|
|
|
|
|
|
|
|
|
/**
|
2024-07-19 00:08:28 -04:00
|
|
|
* This class provides an easy way to link actions to active high logic signals. Each object
|
2022-06-09 08:16:51 +03:00
|
|
|
* represents a digital signal to which callback actions can be bound using {@link
|
|
|
|
|
* #ifHigh(Runnable)}.
|
|
|
|
|
*
|
2024-07-19 00:08:28 -04:00
|
|
|
* <p>BooleanEvents can easily be composed for advanced functionality using {@link
|
|
|
|
|
* #and(BooleanSupplier)}, {@link #or(BooleanSupplier)}, and {@link #negate()}.
|
2022-06-09 08:16:51 +03:00
|
|
|
*
|
2024-07-19 00:08:28 -04:00
|
|
|
* <p>To get a new BooleanEvent that triggers when this one changes see {@link #falling()} and
|
|
|
|
|
* {@link #rising()}.
|
2022-06-09 08:16:51 +03:00
|
|
|
*/
|
|
|
|
|
public class BooleanEvent implements BooleanSupplier {
|
|
|
|
|
/** Poller loop. */
|
|
|
|
|
protected final EventLoop m_loop;
|
2023-08-18 19:18:33 -07:00
|
|
|
|
2022-06-09 08:16:51 +03:00
|
|
|
private final BooleanSupplier m_signal;
|
|
|
|
|
|
2025-05-12 07:19:54 -07:00
|
|
|
/** The state of the condition in the current loop poll. */
|
2023-10-05 00:22:57 -04:00
|
|
|
private final AtomicBoolean m_state = new AtomicBoolean(false);
|
|
|
|
|
|
2022-06-09 08:16:51 +03:00
|
|
|
/**
|
2024-07-19 00:08:28 -04:00
|
|
|
* Creates a new event that is active when the condition is true.
|
2022-06-09 08:16:51 +03:00
|
|
|
*
|
2024-07-19 00:08:28 -04:00
|
|
|
* @param loop the loop that polls this event.
|
2022-06-09 08:16:51 +03:00
|
|
|
* @param signal the digital signal represented by this object.
|
|
|
|
|
*/
|
|
|
|
|
public BooleanEvent(EventLoop loop, BooleanSupplier signal) {
|
|
|
|
|
m_loop = requireNonNullParam(loop, "loop", "BooleanEvent");
|
|
|
|
|
m_signal = requireNonNullParam(signal, "signal", "BooleanEvent");
|
2023-10-05 00:22:57 -04:00
|
|
|
m_state.set(m_signal.getAsBoolean());
|
|
|
|
|
m_loop.bind(() -> m_state.set(m_signal.getAsBoolean()));
|
2022-06-09 08:16:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-07-19 00:08:28 -04:00
|
|
|
* Returns the state of this signal (high or low) as of the last loop poll.
|
2022-06-09 08:16:51 +03:00
|
|
|
*
|
2023-10-05 00:22:57 -04:00
|
|
|
* @return true for the high state, false for the low state. If the event was never polled, it
|
|
|
|
|
* returns the state at event construction.
|
2022-06-09 08:16:51 +03:00
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public final boolean getAsBoolean() {
|
2023-10-05 00:22:57 -04:00
|
|
|
return m_state.get();
|
2022-06-09 08:16:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bind an action to this event.
|
|
|
|
|
*
|
|
|
|
|
* @param action the action to run if this event is active.
|
|
|
|
|
*/
|
|
|
|
|
public final void ifHigh(Runnable action) {
|
2022-11-28 23:48:48 +02:00
|
|
|
m_loop.bind(
|
|
|
|
|
() -> {
|
2023-10-05 00:22:57 -04:00
|
|
|
if (m_state.get()) {
|
2022-11-28 23:48:48 +02:00
|
|
|
action.run();
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-06-09 08:16:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-07-19 00:08:28 -04:00
|
|
|
* A method to "downcast" a BooleanEvent instance to a subclass (for example, to a command-based
|
|
|
|
|
* version of this class).
|
|
|
|
|
*
|
|
|
|
|
* @param ctor a method reference to the constructor of the subclass that accepts the loop as the
|
|
|
|
|
* first parameter and the condition/signal as the second.
|
|
|
|
|
* @param <T> the subclass type
|
|
|
|
|
* @return an instance of the subclass.
|
|
|
|
|
*/
|
|
|
|
|
public <T extends BooleanSupplier> T castTo(BiFunction<EventLoop, BooleanSupplier, T> ctor) {
|
|
|
|
|
return ctor.apply(m_loop, m_state::get);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new event that is active when this event is inactive.
|
|
|
|
|
*
|
|
|
|
|
* @return the new event.
|
|
|
|
|
*/
|
|
|
|
|
public BooleanEvent negate() {
|
|
|
|
|
return new BooleanEvent(m_loop, () -> !m_state.get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Composes this event with another event, returning a new event that is active when both events
|
|
|
|
|
* are active.
|
|
|
|
|
*
|
|
|
|
|
* <p>The events must use the same event loop. If the events use different event loops, the
|
|
|
|
|
* composed signal won't update until both loops are polled.
|
|
|
|
|
*
|
|
|
|
|
* @param other the event to compose with.
|
|
|
|
|
* @return the new event.
|
|
|
|
|
*/
|
|
|
|
|
public BooleanEvent and(BooleanSupplier other) {
|
|
|
|
|
requireNonNullParam(other, "other", "and");
|
|
|
|
|
return new BooleanEvent(m_loop, () -> m_state.get() && other.getAsBoolean());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Composes this event with another event, returning a new event that is active when either event
|
|
|
|
|
* is active.
|
|
|
|
|
*
|
|
|
|
|
* <p>The events must use the same event loop. If the events use different event loops, the
|
|
|
|
|
* composed signal won't update until both loops are polled.
|
|
|
|
|
*
|
|
|
|
|
* @param other the event to compose with.
|
|
|
|
|
* @return the new event.
|
|
|
|
|
*/
|
|
|
|
|
public BooleanEvent or(BooleanSupplier other) {
|
|
|
|
|
requireNonNullParam(other, "other", "or");
|
|
|
|
|
return new BooleanEvent(m_loop, () -> m_state.get() || other.getAsBoolean());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new event that triggers when this one changes from false to true.
|
2022-06-09 08:16:51 +03:00
|
|
|
*
|
2024-07-19 00:08:28 -04:00
|
|
|
* @return the new event.
|
2022-06-09 08:16:51 +03:00
|
|
|
*/
|
|
|
|
|
public BooleanEvent rising() {
|
|
|
|
|
return new BooleanEvent(
|
|
|
|
|
m_loop,
|
|
|
|
|
new BooleanSupplier() {
|
2023-10-05 00:22:57 -04:00
|
|
|
private boolean m_previous = m_state.get();
|
2022-06-09 08:16:51 +03:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean getAsBoolean() {
|
2023-10-05 00:22:57 -04:00
|
|
|
boolean present = m_state.get();
|
2022-06-09 08:16:51 +03:00
|
|
|
boolean ret = !m_previous && present;
|
|
|
|
|
m_previous = present;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-07-19 00:08:28 -04:00
|
|
|
* Creates a new event that triggers when this one changes from true to false.
|
2022-06-09 08:16:51 +03:00
|
|
|
*
|
2024-07-19 00:08:28 -04:00
|
|
|
* @return the event.
|
2022-06-09 08:16:51 +03:00
|
|
|
*/
|
|
|
|
|
public BooleanEvent falling() {
|
|
|
|
|
return new BooleanEvent(
|
|
|
|
|
m_loop,
|
|
|
|
|
new BooleanSupplier() {
|
2023-10-05 00:22:57 -04:00
|
|
|
private boolean m_previous = m_state.get();
|
2022-06-09 08:16:51 +03:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean getAsBoolean() {
|
2023-10-05 00:22:57 -04:00
|
|
|
boolean present = m_state.get();
|
2022-06-09 08:16:51 +03:00
|
|
|
boolean ret = m_previous && !present;
|
|
|
|
|
m_previous = present;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new debounced event from this event - it will become active when this event has been
|
|
|
|
|
* active for longer than the specified period.
|
|
|
|
|
*
|
2025-10-29 03:18:55 +00:00
|
|
|
* @param period The debounce period.
|
|
|
|
|
* @return The debounced event (rising edges debounced only).
|
|
|
|
|
*/
|
|
|
|
|
public BooleanEvent debounce(Time period) {
|
|
|
|
|
return debounce(period.in(Seconds));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new debounced event from this event - it will become active when this event has been
|
|
|
|
|
* active for longer than the specified period.
|
|
|
|
|
*
|
|
|
|
|
* @param seconds The debounce period in seconds.
|
2024-07-19 00:08:28 -04:00
|
|
|
* @return The debounced event (rising edges debounced only).
|
2022-06-09 08:16:51 +03:00
|
|
|
*/
|
|
|
|
|
public BooleanEvent debounce(double seconds) {
|
|
|
|
|
return debounce(seconds, Debouncer.DebounceType.kRising);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new debounced event from this event - it will become active when this event has been
|
|
|
|
|
* active for longer than the specified period.
|
|
|
|
|
*
|
2025-10-29 03:18:55 +00:00
|
|
|
* @param period The debounce period.
|
|
|
|
|
* @param type The debounce type.
|
|
|
|
|
* @return The debounced event.
|
|
|
|
|
*/
|
|
|
|
|
public BooleanEvent debounce(Time period, Debouncer.DebounceType type) {
|
|
|
|
|
return debounce(period.in(Seconds), type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new debounced event from this event - it will become active when this event has been
|
|
|
|
|
* active for longer than the specified period.
|
|
|
|
|
*
|
|
|
|
|
* @param seconds The debounce period in seconds.
|
2022-06-09 08:16:51 +03:00
|
|
|
* @param type The debounce type.
|
|
|
|
|
* @return The debounced event.
|
|
|
|
|
*/
|
|
|
|
|
public BooleanEvent debounce(double seconds, Debouncer.DebounceType type) {
|
|
|
|
|
return new BooleanEvent(
|
|
|
|
|
m_loop,
|
|
|
|
|
new BooleanSupplier() {
|
|
|
|
|
private final Debouncer m_debouncer = new Debouncer(seconds, type);
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean getAsBoolean() {
|
2023-10-05 00:22:57 -04:00
|
|
|
return m_debouncer.calculate(m_state.get());
|
2022-06-09 08:16:51 +03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|