22import inspect
33import logging
44
5+ import hal
56import wpilib
67
78from networktables import NetworkTables
89from wpilib .shuffleboard import Shuffleboard
910
10- from robotpy_ext .misc import NotifierDelay
1111from robotpy_ext .autonomous import AutonomousModeSelector
12-
12+ from robotpy_ext . misc import NotifierDelay
1313from robotpy_ext .misc .orderedclass import OrderedClass
1414from robotpy_ext .misc .annotations import get_class_annotations
1515
@@ -34,8 +34,8 @@ class MagicRobot(wpilib.SampleRobot, metaclass=OrderedClass):
3434
3535 MagicRobot uses the :class:`.AutonomousModeSelector` to allow you
3636 to define multiple autonomous modes and to select one of them via
37- the SmartDashboard/SFX .
38-
37+ the SmartDashboard/Shuffleboard .
38+
3939 MagicRobot will set the following NetworkTables variables
4040 automatically:
4141
@@ -78,6 +78,8 @@ def robotInit(self):
7878 self .__nt .putBoolean ("is_simulation" , self .isSimulation ())
7979 self .__nt .putBoolean ("is_ds_attached" , self .ds .isDSAttached ())
8080
81+ self .watchdog = wpilib .Watchdog (self .control_loop_wait_time , self ._loop_overrun )
82+
8183 def createObjects (self ):
8284 """
8385 You should override this and initialize all of your wpilib
@@ -183,9 +185,13 @@ def robotPeriodic(self):
183185 The default implementation will update
184186 SmartDashboard, LiveWindow and Shuffleboard.
185187 """
188+ watchdog = self .watchdog
186189 wpilib .SmartDashboard .updateValues ()
190+ watchdog .addEpoch ("SmartDashboard" )
187191 wpilib .LiveWindow .updateValues ()
192+ watchdog .addEpoch ("LiveWindow" )
188193 Shuffleboard .update ()
194+ watchdog .addEpoch ("Shuffleboard" )
189195
190196 def onException (self , forceReport = False ):
191197 """
@@ -289,6 +295,7 @@ def autonomous(self):
289295 self .control_loop_wait_time ,
290296 (self ._execute_components , self ._update_feedback , self .robotPeriodic ),
291297 self .onException ,
298+ watchdog = self .watchdog ,
292299 )
293300
294301 self ._on_mode_disable_components ()
@@ -301,6 +308,8 @@ def disabled(self):
301308
302309 .. warning:: Internal API, don't override
303310 """
311+ watchdog = self .watchdog
312+ watchdog .reset ()
304313
305314 self .__nt .putString ("mode" , "disabled" )
306315 ds_attached = None
@@ -311,21 +320,31 @@ def disabled(self):
311320 self .disabledInit ()
312321 except :
313322 self .onException (forceReport = True )
323+ watchdog .addEpoch ("disabledInit()" )
314324
315325 with NotifierDelay (self .control_loop_wait_time ) as delay :
316326 while self .isDisabled ():
317327 if ds_attached != self .ds .isDSAttached ():
318328 ds_attached = not ds_attached
319329 self .__nt .putBoolean ("is_ds_attached" , ds_attached )
320330
331+ hal .observeUserProgramDisabled ()
321332 try :
322333 self .disabledPeriodic ()
323334 except :
324335 self .onException ()
336+ watchdog .addEpoch ("disabledPeriodic()" )
325337
326338 self ._update_feedback ()
327339 self .robotPeriodic ()
340+ watchdog .addEpoch ("robotPeriodic()" )
341+ watchdog .disable ()
342+
343+ if watchdog .isExpired ():
344+ watchdog .printEpochs ()
345+
328346 delay .wait ()
347+ watchdog .reset ()
329348
330349 def operatorControl (self ):
331350 """
@@ -335,6 +354,8 @@ def operatorControl(self):
335354
336355 .. warning:: Internal API, don't override
337356 """
357+ watchdog = self .watchdog
358+ watchdog .reset ()
338359
339360 self .__nt .putString ("mode" , "teleop" )
340361 # don't need to update this during teleop -- presumably will switch
@@ -349,24 +370,36 @@ def operatorControl(self):
349370 self .teleopInit ()
350371 except :
351372 self .onException (forceReport = True )
373+ watchdog .addEpoch ("teleopInit()" )
352374
353375 with NotifierDelay (self .control_loop_wait_time ) as delay :
354376 while self .isOperatorControl () and self .isEnabled ():
377+ hal .observeUserProgramTeleop ()
355378 try :
356379 self .teleopPeriodic ()
357380 except :
358381 self .onException ()
382+ watchdog .addEpoch ("teleopPeriodic()" )
359383
360384 self ._execute_components ()
385+
361386 self ._update_feedback ()
362387 self .robotPeriodic ()
388+ watchdog .addEpoch ("robotPeriodic()" )
389+ watchdog .disable ()
390+
391+ if watchdog .isExpired ():
392+ watchdog .printEpochs ()
363393
364394 delay .wait ()
395+ watchdog .reset ()
365396
366397 self ._on_mode_disable_components ()
367398
368399 def test (self ):
369400 """Called when the robot is in test mode"""
401+ watchdog = self .watchdog
402+ watchdog .reset ()
370403
371404 self .__nt .putString ("mode" , "test" )
372405 self .__nt .putBoolean ("is_ds_attached" , self .ds .isDSAttached ())
@@ -376,33 +409,45 @@ def test(self):
376409 self .testInit ()
377410 except :
378411 self .onException (forceReport = True )
412+ watchdog .addEpoch ("testInit()" )
379413
380414 with NotifierDelay (self .control_loop_wait_time ) as delay :
381415 while self .isTest () and self .isEnabled ():
416+ hal .observeUserProgramTest ()
382417 try :
383418 self .testPeriodic ()
384419 except :
385420 self .onException ()
421+ watchdog .addEpoch ("testPeriodic()" )
386422
387423 self ._update_feedback ()
388424 self .robotPeriodic ()
425+ watchdog .addEpoch ("robotPeriodic()" )
426+ watchdog .disable ()
427+
428+ if watchdog .isExpired ():
429+ watchdog .printEpochs ()
430+
389431 delay .wait ()
432+ watchdog .reset ()
390433
391434 def _on_mode_enable_components (self ):
392435 # initialize things
393- for component in self ._components :
394- if hasattr (component , "on_enable" ):
436+ for _ , component in self ._components :
437+ on_enable = getattr (component , "on_enable" , None )
438+ if on_enable is not None :
395439 try :
396- component . on_enable ()
440+ on_enable ()
397441 except :
398442 self .onException (forceReport = True )
399443
400444 def _on_mode_disable_components (self ):
401445 # deinitialize things
402- for component in self ._components :
403- if hasattr (component , "on_disable" ):
446+ for _ , component in self ._components :
447+ on_disable = getattr (component , "on_disable" , None )
448+ if on_disable is not None :
404449 try :
405- component . on_disable ()
450+ on_disable ()
406451 except :
407452 self .onException (forceReport = True )
408453
@@ -492,7 +537,6 @@ def _create_components(self):
492537
493538 # For each new component, perform magic injection
494539 for cname , component in components :
495- self ._components .append (component )
496540 setup_tunables (component , cname , "components" )
497541 self ._feedbacks += collect_feedbacks (component , cname , "components" )
498542 self ._setup_vars (cname , component )
@@ -518,6 +562,8 @@ def _create_components(self):
518562 if hasattr (mode , "setup" ):
519563 mode .setup ()
520564
565+ self ._components = components
566+
521567 def _create_component (self , name , ctyp ):
522568 # Create instance, set it on self
523569 component = ctyp ()
@@ -638,13 +684,19 @@ def _update_feedback(self):
638684 self .onException ()
639685 continue
640686 entry .setValue (value )
687+ self .watchdog .addEpoch ("@magicbot.feedback" )
641688
642689 def _execute_components (self ):
643- for component in self ._components :
690+ for name , component in self ._components :
644691 try :
645692 component .execute ()
646693 except :
647694 self .onException ()
695+ self .watchdog .addEpoch (name )
648696
649697 for reset_dict , component in self ._reset_components :
650698 component .__dict__ .update (reset_dict )
699+
700+ def _loop_overrun (self ):
701+ # TODO: print something here without making it extremely annoying
702+ pass
0 commit comments