001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.wpilibj; 006 007import edu.wpi.first.hal.DriverStationJNI; 008import edu.wpi.first.hal.HAL; 009import edu.wpi.first.networktables.NetworkTableInstance; 010import edu.wpi.first.wpilibj.livewindow.LiveWindow; 011import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; 012import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; 013import java.util.ConcurrentModificationException; 014 015/** 016 * IterativeRobotBase implements a specific type of robot program framework, extending the RobotBase 017 * class. 018 * 019 * <p>The IterativeRobotBase class does not implement startCompetition(), so it should not be used 020 * by teams directly. 021 * 022 * <p>This class provides the following functions which are called by the main loop, 023 * startCompetition(), at the appropriate times: 024 * 025 * <p>robotInit() -- provide for initialization at robot power-on 026 * 027 * <p>init() functions -- each of the following functions is called once when the appropriate mode 028 * is entered: 029 * 030 * <ul> 031 * <li>disabledInit() -- called each and every time disabled is entered from another mode 032 * <li>autonomousInit() -- called each and every time autonomous is entered from another mode 033 * <li>teleopInit() -- called each and every time teleop is entered from another mode 034 * <li>testInit() -- called each and every time test is entered from another mode 035 * </ul> 036 * 037 * <p>periodic() functions -- each of these functions is called on an interval: 038 * 039 * <ul> 040 * <li>robotPeriodic() 041 * <li>disabledPeriodic() 042 * <li>autonomousPeriodic() 043 * <li>teleopPeriodic() 044 * <li>testPeriodic() 045 * </ul> 046 * 047 * <p>exit() functions -- each of the following functions is called once when the appropriate mode 048 * is exited: 049 * 050 * <ul> 051 * <li>disabledExit() -- called each and every time disabled is exited 052 * <li>autonomousExit() -- called each and every time autonomous is exited 053 * <li>teleopExit() -- called each and every time teleop is exited 054 * <li>testExit() -- called each and every time test is exited 055 * </ul> 056 */ 057public abstract class IterativeRobotBase extends RobotBase { 058 private enum Mode { 059 kNone, 060 kDisabled, 061 kAutonomous, 062 kTeleop, 063 kTest 064 } 065 066 private final DSControlWord m_word = new DSControlWord(); 067 private Mode m_lastMode = Mode.kNone; 068 private final double m_period; 069 private final Watchdog m_watchdog; 070 private boolean m_ntFlushEnabled = true; 071 private boolean m_lwEnabledInTest = true; 072 073 /** 074 * Constructor for IterativeRobotBase. 075 * 076 * @param period Period in seconds. 077 */ 078 protected IterativeRobotBase(double period) { 079 m_period = period; 080 m_watchdog = new Watchdog(period, this::printLoopOverrunMessage); 081 } 082 083 /** Provide an alternate "main loop" via startCompetition(). */ 084 @Override 085 public abstract void startCompetition(); 086 087 /* ----------- Overridable initialization code ----------------- */ 088 089 /** 090 * Robot-wide initialization code should go here. 091 * 092 * <p>Users should override this method for default Robot-wide initialization which will be called 093 * when the robot is first powered on. It will be called exactly one time. 094 * 095 * <p>Warning: the Driver Station "Robot Code" light and FMS "Robot Ready" indicators will be off 096 * until RobotInit() exits. Code in RobotInit() that waits for enable will cause the robot to 097 * never indicate that the code is ready, causing the robot to be bypassed in a match. 098 */ 099 public void robotInit() {} 100 101 /** 102 * Robot-wide simulation initialization code should go here. 103 * 104 * <p>Users should override this method for default Robot-wide simulation related initialization 105 * which will be called when the robot is first started. It will be called exactly one time after 106 * RobotInit is called only when the robot is in simulation. 107 */ 108 public void simulationInit() {} 109 110 /** 111 * Initialization code for disabled mode should go here. 112 * 113 * <p>Users should override this method for initialization code which will be called each time the 114 * robot enters disabled mode. 115 */ 116 public void disabledInit() {} 117 118 /** 119 * Initialization code for autonomous mode should go here. 120 * 121 * <p>Users should override this method for initialization code which will be called each time the 122 * robot enters autonomous mode. 123 */ 124 public void autonomousInit() {} 125 126 /** 127 * Initialization code for teleop mode should go here. 128 * 129 * <p>Users should override this method for initialization code which will be called each time the 130 * robot enters teleop mode. 131 */ 132 public void teleopInit() {} 133 134 /** 135 * Initialization code for test mode should go here. 136 * 137 * <p>Users should override this method for initialization code which will be called each time the 138 * robot enters test mode. 139 */ 140 public void testInit() {} 141 142 /* ----------- Overridable periodic code ----------------- */ 143 144 private boolean m_rpFirstRun = true; 145 146 /** Periodic code for all robot modes should go here. */ 147 public void robotPeriodic() { 148 if (m_rpFirstRun) { 149 System.out.println("Default robotPeriodic() method... Override me!"); 150 m_rpFirstRun = false; 151 } 152 } 153 154 private boolean m_spFirstRun = true; 155 156 /** 157 * Periodic simulation code should go here. 158 * 159 * <p>This function is called in a simulated robot after user code executes. 160 */ 161 public void simulationPeriodic() { 162 if (m_spFirstRun) { 163 System.out.println("Default simulationPeriodic() method... Override me!"); 164 m_spFirstRun = false; 165 } 166 } 167 168 private boolean m_dpFirstRun = true; 169 170 /** Periodic code for disabled mode should go here. */ 171 public void disabledPeriodic() { 172 if (m_dpFirstRun) { 173 System.out.println("Default disabledPeriodic() method... Override me!"); 174 m_dpFirstRun = false; 175 } 176 } 177 178 private boolean m_apFirstRun = true; 179 180 /** Periodic code for autonomous mode should go here. */ 181 public void autonomousPeriodic() { 182 if (m_apFirstRun) { 183 System.out.println("Default autonomousPeriodic() method... Override me!"); 184 m_apFirstRun = false; 185 } 186 } 187 188 private boolean m_tpFirstRun = true; 189 190 /** Periodic code for teleop mode should go here. */ 191 public void teleopPeriodic() { 192 if (m_tpFirstRun) { 193 System.out.println("Default teleopPeriodic() method... Override me!"); 194 m_tpFirstRun = false; 195 } 196 } 197 198 private boolean m_tmpFirstRun = true; 199 200 /** Periodic code for test mode should go here. */ 201 public void testPeriodic() { 202 if (m_tmpFirstRun) { 203 System.out.println("Default testPeriodic() method... Override me!"); 204 m_tmpFirstRun = false; 205 } 206 } 207 208 /** 209 * Exit code for disabled mode should go here. 210 * 211 * <p>Users should override this method for code which will be called each time the robot exits 212 * disabled mode. 213 */ 214 public void disabledExit() {} 215 216 /** 217 * Exit code for autonomous mode should go here. 218 * 219 * <p>Users should override this method for code which will be called each time the robot exits 220 * autonomous mode. 221 */ 222 public void autonomousExit() {} 223 224 /** 225 * Exit code for teleop mode should go here. 226 * 227 * <p>Users should override this method for code which will be called each time the robot exits 228 * teleop mode. 229 */ 230 public void teleopExit() {} 231 232 /** 233 * Exit code for test mode should go here. 234 * 235 * <p>Users should override this method for code which will be called each time the robot exits 236 * test mode. 237 */ 238 public void testExit() {} 239 240 /** 241 * Enables or disables flushing NetworkTables every loop iteration. By default, this is enabled. 242 * 243 * @param enabled True to enable, false to disable 244 */ 245 public void setNetworkTablesFlushEnabled(boolean enabled) { 246 m_ntFlushEnabled = enabled; 247 } 248 249 /** 250 * Sets whether LiveWindow operation is enabled during test mode. Calling 251 * 252 * @param testLW True to enable, false to disable. Defaults to true. 253 * @throws ConcurrentModificationException if this is called during test mode. 254 */ 255 public void enableLiveWindowInTest(boolean testLW) { 256 if (isTestEnabled()) { 257 throw new ConcurrentModificationException("Can't configure test mode while in test mode!"); 258 } 259 m_lwEnabledInTest = testLW; 260 } 261 262 /** 263 * Whether LiveWindow operation is enabled during test mode. 264 * 265 * @return whether LiveWindow should be enabled in test mode. 266 */ 267 public boolean isLiveWindowEnabledInTest() { 268 return m_lwEnabledInTest; 269 } 270 271 /** 272 * Gets time period between calls to Periodic() functions. 273 * 274 * @return The time period between calls to Periodic() functions. 275 */ 276 public double getPeriod() { 277 return m_period; 278 } 279 280 protected void loopFunc() { 281 DriverStation.refreshData(); 282 m_watchdog.reset(); 283 284 m_word.refresh(); 285 286 // Get current mode 287 Mode mode = Mode.kNone; 288 if (m_word.isDisabled()) { 289 mode = Mode.kDisabled; 290 } else if (m_word.isAutonomous()) { 291 mode = Mode.kAutonomous; 292 } else if (m_word.isTeleop()) { 293 mode = Mode.kTeleop; 294 } else if (m_word.isTest()) { 295 mode = Mode.kTest; 296 } 297 298 // If mode changed, call mode exit and entry functions 299 if (m_lastMode != mode) { 300 // Call last mode's exit function 301 if (m_lastMode == Mode.kDisabled) { 302 disabledExit(); 303 } else if (m_lastMode == Mode.kAutonomous) { 304 autonomousExit(); 305 } else if (m_lastMode == Mode.kTeleop) { 306 teleopExit(); 307 } else if (m_lastMode == Mode.kTest) { 308 if (m_lwEnabledInTest) { 309 LiveWindow.setEnabled(false); 310 Shuffleboard.disableActuatorWidgets(); 311 } 312 testExit(); 313 } 314 315 // Call current mode's entry function 316 if (mode == Mode.kDisabled) { 317 disabledInit(); 318 m_watchdog.addEpoch("disabledInit()"); 319 } else if (mode == Mode.kAutonomous) { 320 autonomousInit(); 321 m_watchdog.addEpoch("autonomousInit()"); 322 } else if (mode == Mode.kTeleop) { 323 teleopInit(); 324 m_watchdog.addEpoch("teleopInit()"); 325 } else if (mode == Mode.kTest) { 326 if (m_lwEnabledInTest) { 327 LiveWindow.setEnabled(true); 328 Shuffleboard.enableActuatorWidgets(); 329 } 330 testInit(); 331 m_watchdog.addEpoch("testInit()"); 332 } 333 334 m_lastMode = mode; 335 } 336 337 // Call the appropriate function depending upon the current robot mode 338 if (mode == Mode.kDisabled) { 339 DriverStationJNI.observeUserProgramDisabled(); 340 disabledPeriodic(); 341 m_watchdog.addEpoch("disabledPeriodic()"); 342 } else if (mode == Mode.kAutonomous) { 343 DriverStationJNI.observeUserProgramAutonomous(); 344 autonomousPeriodic(); 345 m_watchdog.addEpoch("autonomousPeriodic()"); 346 } else if (mode == Mode.kTeleop) { 347 DriverStationJNI.observeUserProgramTeleop(); 348 teleopPeriodic(); 349 m_watchdog.addEpoch("teleopPeriodic()"); 350 } else { 351 DriverStationJNI.observeUserProgramTest(); 352 testPeriodic(); 353 m_watchdog.addEpoch("testPeriodic()"); 354 } 355 356 robotPeriodic(); 357 m_watchdog.addEpoch("robotPeriodic()"); 358 359 SmartDashboard.updateValues(); 360 m_watchdog.addEpoch("SmartDashboard.updateValues()"); 361 LiveWindow.updateValues(); 362 m_watchdog.addEpoch("LiveWindow.updateValues()"); 363 Shuffleboard.update(); 364 m_watchdog.addEpoch("Shuffleboard.update()"); 365 366 if (isSimulation()) { 367 HAL.simPeriodicBefore(); 368 simulationPeriodic(); 369 HAL.simPeriodicAfter(); 370 m_watchdog.addEpoch("simulationPeriodic()"); 371 } 372 373 m_watchdog.disable(); 374 375 // Flush NetworkTables 376 if (m_ntFlushEnabled) { 377 NetworkTableInstance.getDefault().flushLocal(); 378 } 379 380 // Warn on loop time overruns 381 if (m_watchdog.isExpired()) { 382 m_watchdog.printEpochs(); 383 } 384 } 385 386 private void printLoopOverrunMessage() { 387 DriverStation.reportWarning("Loop time of " + m_period + "s overrun\n", false); 388 } 389}