package jaci.openrio.toast.core; import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary; import edu.wpi.first.wpilibj.hal.HAL; import jaci.openrio.toast.core.loader.verification.VerificationWorker; import jaci.openrio.toast.lib.FRCHooks; import jaci.openrio.toast.lib.state.ConcurrentVector; import jaci.openrio.toast.lib.state.RobotState; import jaci.openrio.toast.lib.state.StateListener; import static jaci.openrio.toast.lib.state.RobotState.*; /** * Keeps track of the {@link jaci.openrio.toast.lib.state.RobotState} the robot is in, as well as the one * it just switched from. This allows for context-aware state management. * <p> * This class also allows classes to implement sub-interfaces of {@link jaci.openrio.toast.lib.state.StateListener}, * which will trigger the interfaces when the robot 'ticks' or transitions between states. This allows for multiple * handlers to work with states * * @author Jaci */ public class StateTracker { static boolean _state_disabled_init = false; static boolean _state_autonomous_init = false; static boolean _state_teleop_init = false; static boolean _state_test_init = false; public static RobotState currentState; public static RobotState lastState; private static Toast impl; private static volatile ConcurrentVector<StateListener.Ticker> tickers = new ConcurrentVector<StateListener.Ticker>(); private static volatile ConcurrentVector<StateListener.Transition> transitioners = new ConcurrentVector<StateListener.Transition>(); /** * Start the StateTracker loop */ public static void init(Toast impl) { HAL.report(FRCNetworkCommunicationsLibrary.tResourceType.kResourceType_Framework, 1); StateTracker.impl = impl; boolean isAlive; if (ToastBootstrap.isVerification) VerificationWorker.begin(); while (true) { if (impl.isDisabled()) { if (!_state_disabled_init) { transition(DISABLED); _state_disabled_init = true; _state_autonomous_init = false; _state_teleop_init = false; _state_test_init = false; } if (nextPeriodReady()) { FRCHooks.observeDisabled(); tick(RobotState.DISABLED); } } else if (impl.isAutonomous()) { if (!_state_autonomous_init) { transition(AUTONOMOUS); _state_autonomous_init = true; _state_disabled_init = false; _state_teleop_init = false; _state_test_init = false; } if (nextPeriodReady()) { FRCHooks.observeAutonomous(); tick(RobotState.AUTONOMOUS); } } else if (impl.isTest()) { if (!_state_test_init) { transition(TEST); _state_test_init = true; _state_disabled_init = false; _state_autonomous_init = false; _state_teleop_init = false; } if (nextPeriodReady()) { FRCHooks.observeTest(); tick(RobotState.TEST); } } else { //Teleop if (!_state_teleop_init) { transition(TELEOP); _state_teleop_init = true; _state_disabled_init = false; _state_autonomous_init = false; _state_test_init = false; } if (nextPeriodReady()) { FRCHooks.observeTeleop(); tick(RobotState.TELEOP); } } impl.station().waitForData(); } } /** * Transition between the old state and the new (given) state */ static void transition(RobotState state) { lastState = currentState; currentState = state; transitioners.tick(); for (StateListener.Transition tra : transitioners) tra.transitionState(currentState, lastState); } /** * Tick all interfaces with the given state */ public static void tick(RobotState state) { tickers.tick(); for (StateListener.Ticker ticker : tickers) ticker.tickState(state); } /** * Register a new 'Ticking' {@link jaci.openrio.toast.lib.state.StateListener}. This will tick * whenever a state has an update, or every 20ms. This is similar to the {@link edu.wpi.first.wpilibj.IterativeRobot} * implementation */ public static void addTicker(StateListener.Ticker ticker) { tickers.addConcurrent(ticker); } /** * Register a new 'Transition' {@link jaci.openrio.toast.lib.state.StateListener}. This will * trigger whenever the robot switches between states. */ public static void addTransition(StateListener.Transition transition) { transitioners.addConcurrent(transition); } /** * Remove a {@link jaci.openrio.toast.lib.state.StateListener.Ticker} from the * StateTracker */ public static void removeTicker(StateListener.Ticker ticker) { tickers.removeConcurrent(ticker); } /** * Remove a {@link jaci.openrio.toast.lib.state.StateListener.Transition} from the * StateTracker */ public static void removeTransition(StateListener.Transition transition) { transitioners.removeConcurrent(transition); } /** * Waits for new control data to be received. */ private static boolean nextPeriodReady() { return impl.station().isNewControlData(); } }