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 org.wpilib.framework; 006 007import org.wpilib.driverstation.DriverStationErrors; 008import org.wpilib.driverstation.internal.DriverStationBackend; 009import org.wpilib.hardware.hal.ControlWord; 010import org.wpilib.hardware.hal.DriverStationJNI; 011import org.wpilib.hardware.hal.HAL; 012import org.wpilib.hardware.hal.RobotMode; 013import org.wpilib.networktables.NetworkTableInstance; 014import org.wpilib.smartdashboard.SmartDashboard; 015import org.wpilib.system.Watchdog; 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>driverStationConnected() -- provide for initialization the first time the DS is connected 028 * 029 * <p>init() functions -- each of the following functions is called once when the appropriate mode 030 * is entered: 031 * 032 * <ul> 033 * <li>disabledInit() -- called each and every time disabled is entered from another mode 034 * <li>autonomousInit() -- called each and every time autonomous is entered from another mode 035 * <li>teleopInit() -- called each and every time teleop is entered from another mode 036 * <li>utilityInit() -- called each and every time utility is entered from another mode 037 * </ul> 038 * 039 * <p>periodic() functions -- each of these functions is called on an interval: 040 * 041 * <ul> 042 * <li>robotPeriodic() 043 * <li>disabledPeriodic() 044 * <li>autonomousPeriodic() 045 * <li>teleopPeriodic() 046 * <li>utilityPeriodic() 047 * </ul> 048 * 049 * <p>exit() functions -- each of the following functions is called once when the appropriate mode 050 * is exited: 051 * 052 * <ul> 053 * <li>disabledExit() -- called each and every time disabled is exited 054 * <li>autonomousExit() -- called each and every time autonomous is exited 055 * <li>teleopExit() -- called each and every time teleop is exited 056 * <li>utilityExit() -- called each and every time utility is exited 057 * </ul> 058 */ 059public abstract class IterativeRobotBase extends RobotBase { 060 private final ControlWord m_word = new ControlWord(); 061 private RobotMode m_lastMode; 062 private final double m_period; 063 private final Watchdog m_watchdog; 064 private boolean m_calledDsConnected; 065 066 /** 067 * Constructor for IterativeRobotBase. 068 * 069 * @param period Period in seconds. 070 */ 071 protected IterativeRobotBase(double period) { 072 m_period = period; 073 m_watchdog = new Watchdog(period, this::printLoopOverrunMessage); 074 } 075 076 /** Provide an alternate "main loop" via startCompetition(). */ 077 @Override 078 public abstract void startCompetition(); 079 080 /* ----------- Overridable initialization code ----------------- */ 081 082 /** 083 * Code that needs to know the DS state should go here. 084 * 085 * <p>Users should override this method for initialization that needs to occur after the DS is 086 * connected, such as needing the alliance information. 087 */ 088 public void driverStationConnected() {} 089 090 /** 091 * Robot-wide simulation initialization code should go here. 092 * 093 * <p>Users should override this method for default Robot-wide simulation related initialization 094 * which will be called when the robot is first started. It will be called exactly one time after 095 * the robot class constructor is called only when the robot is in simulation. 096 */ 097 public void simulationInit() {} 098 099 /** 100 * Initialization code for disabled mode should go here. 101 * 102 * <p>Users should override this method for initialization code which will be called each time the 103 * robot enters disabled mode. 104 */ 105 public void disabledInit() {} 106 107 /** 108 * Initialization code for autonomous mode should go here. 109 * 110 * <p>Users should override this method for initialization code which will be called each time the 111 * robot enters autonomous mode. 112 */ 113 public void autonomousInit() {} 114 115 /** 116 * Initialization code for teleop mode should go here. 117 * 118 * <p>Users should override this method for initialization code which will be called each time the 119 * robot enters teleop mode. 120 */ 121 public void teleopInit() {} 122 123 /** 124 * Initialization code for utility 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 utility mode. 128 */ 129 public void utilityInit() {} 130 131 /* ----------- Overridable periodic code ----------------- */ 132 133 private boolean m_rpFirstRun = true; 134 135 /** Periodic code for all robot modes should go here. */ 136 public void robotPeriodic() { 137 if (m_rpFirstRun) { 138 System.out.println("Default robotPeriodic() method... Override me!"); 139 m_rpFirstRun = false; 140 } 141 } 142 143 private boolean m_spFirstRun = true; 144 145 /** 146 * Periodic simulation code should go here. 147 * 148 * <p>This function is called in a simulated robot after user code executes. 149 */ 150 public void simulationPeriodic() { 151 if (m_spFirstRun) { 152 System.out.println("Default simulationPeriodic() method... Override me!"); 153 m_spFirstRun = false; 154 } 155 } 156 157 private boolean m_dpFirstRun = true; 158 159 /** Periodic code for disabled mode should go here. */ 160 public void disabledPeriodic() { 161 if (m_dpFirstRun) { 162 System.out.println("Default disabledPeriodic() method... Override me!"); 163 m_dpFirstRun = false; 164 } 165 } 166 167 private boolean m_apFirstRun = true; 168 169 /** Periodic code for autonomous mode should go here. */ 170 public void autonomousPeriodic() { 171 if (m_apFirstRun) { 172 System.out.println("Default autonomousPeriodic() method... Override me!"); 173 m_apFirstRun = false; 174 } 175 } 176 177 private boolean m_tpFirstRun = true; 178 179 /** Periodic code for teleop mode should go here. */ 180 public void teleopPeriodic() { 181 if (m_tpFirstRun) { 182 System.out.println("Default teleopPeriodic() method... Override me!"); 183 m_tpFirstRun = false; 184 } 185 } 186 187 private boolean m_tmpFirstRun = true; 188 189 /** Periodic code for utility mode should go here. */ 190 public void utilityPeriodic() { 191 if (m_tmpFirstRun) { 192 System.out.println("Default utilityPeriodic() method... Override me!"); 193 m_tmpFirstRun = false; 194 } 195 } 196 197 /** 198 * Exit code for disabled mode should go here. 199 * 200 * <p>Users should override this method for code which will be called each time the robot exits 201 * disabled mode. 202 */ 203 public void disabledExit() {} 204 205 /** 206 * Exit code for autonomous mode should go here. 207 * 208 * <p>Users should override this method for code which will be called each time the robot exits 209 * autonomous mode. 210 */ 211 public void autonomousExit() {} 212 213 /** 214 * Exit code for teleop mode should go here. 215 * 216 * <p>Users should override this method for code which will be called each time the robot exits 217 * teleop mode. 218 */ 219 public void teleopExit() {} 220 221 /** 222 * Exit code for utility mode should go here. 223 * 224 * <p>Users should override this method for code which will be called each time the robot exits 225 * utility mode. 226 */ 227 public void utilityExit() {} 228 229 /** 230 * Gets time period between calls to Periodic() functions. 231 * 232 * @return The time period between calls to Periodic() functions. 233 */ 234 public double getPeriod() { 235 return m_period; 236 } 237 238 /** Loop function. */ 239 protected final void loopFunc() { 240 DriverStationBackend.refreshData(); 241 DriverStationBackend.refreshControlWordFromCache(m_word); 242 m_watchdog.reset(); 243 244 // Get current mode; treat disabled as unknown 245 boolean enabled = m_word.isEnabled(); 246 RobotMode mode = enabled ? m_word.getRobotMode() : RobotMode.UNKNOWN; 247 248 if (!m_calledDsConnected && m_word.isDSAttached()) { 249 m_calledDsConnected = true; 250 driverStationConnected(); 251 } 252 253 // If mode changed, call mode exit and entry functions 254 if (m_lastMode != mode) { 255 if (m_lastMode != null) { 256 // Call last mode's exit function 257 switch (m_lastMode) { 258 case UNKNOWN -> disabledExit(); 259 case AUTONOMOUS -> autonomousExit(); 260 case TELEOPERATED -> teleopExit(); 261 case UTILITY -> utilityExit(); 262 default -> { 263 // NOP 264 } 265 } 266 } 267 268 // Call current mode's entry function 269 switch (mode) { 270 case UNKNOWN -> { 271 disabledInit(); 272 m_watchdog.addEpoch("disabledInit()"); 273 } 274 case AUTONOMOUS -> { 275 autonomousInit(); 276 m_watchdog.addEpoch("autonomousInit()"); 277 } 278 case TELEOPERATED -> { 279 teleopInit(); 280 m_watchdog.addEpoch("teleopInit()"); 281 } 282 case UTILITY -> { 283 utilityInit(); 284 m_watchdog.addEpoch("utilityInit()"); 285 } 286 default -> { 287 // NOP 288 } 289 } 290 291 m_lastMode = mode; 292 } 293 294 // Call the appropriate function depending upon the current robot mode 295 DriverStationJNI.observeUserProgram(m_word.getNative()); 296 switch (mode) { 297 case UNKNOWN -> { 298 disabledPeriodic(); 299 m_watchdog.addEpoch("disabledPeriodic()"); 300 } 301 case AUTONOMOUS -> { 302 autonomousPeriodic(); 303 m_watchdog.addEpoch("autonomousPeriodic()"); 304 } 305 case TELEOPERATED -> { 306 teleopPeriodic(); 307 m_watchdog.addEpoch("teleopPeriodic()"); 308 } 309 case UTILITY -> { 310 utilityPeriodic(); 311 m_watchdog.addEpoch("utilityPeriodic()"); 312 } 313 default -> { 314 // NOP 315 } 316 } 317 318 robotPeriodic(); 319 m_watchdog.addEpoch("robotPeriodic()"); 320 321 SmartDashboard.updateValues(); 322 m_watchdog.addEpoch("SmartDashboard.updateValues()"); 323 324 if (isSimulation()) { 325 HAL.simPeriodicBefore(); 326 simulationPeriodic(); 327 HAL.simPeriodicAfter(); 328 m_watchdog.addEpoch("simulationPeriodic()"); 329 } 330 331 m_watchdog.disable(); 332 333 // Flush NetworkTables 334 NetworkTableInstance.getDefault().flushLocal(); 335 336 // Warn on loop time overruns 337 if (m_watchdog.isExpired()) { 338 m_watchdog.printEpochs(); 339 } 340 } 341 342 /** Prints list of epochs added so far and their times. */ 343 public void printWatchdogEpochs() { 344 m_watchdog.printEpochs(); 345 } 346 347 private void printLoopOverrunMessage() { 348 DriverStationErrors.reportWarning("Loop time of " + m_period + "s overrun\n", false); 349 } 350}