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