Skip to content

Commit 718c8d9

Browse files
committed
NotifierDelay: Defensively free the notifier
Allow for using a NotifierDelay as a context manager. Also add a __del__ to doubly ensure this is cleaned up, similarly to Python's IO objects.
1 parent 86b2243 commit 718c8d9

File tree

3 files changed

+66
-60
lines changed

3 files changed

+66
-60
lines changed

magicbot/magicrobot.py

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -295,30 +295,26 @@ def disabled(self):
295295
ds_attached = None
296296
wpilib.LiveWindow.setEnabled(False)
297297

298-
delay = NotifierDelay(self.control_loop_wait_time)
299-
300298
self._on_mode_disable_components()
301299
try:
302300
self.disabledInit()
303301
except:
304302
self.onException(forceReport=True)
305303

306-
while self.isDisabled():
307-
308-
if ds_attached != self.ds.isDSAttached():
309-
ds_attached = not ds_attached
310-
self.__nt.putBoolean('is_ds_attached', ds_attached)
311-
312-
try:
313-
self.disabledPeriodic()
314-
except:
315-
self.onException()
304+
with NotifierDelay(self.control_loop_wait_time) as delay:
305+
while self.isDisabled():
306+
if ds_attached != self.ds.isDSAttached():
307+
ds_attached = not ds_attached
308+
self.__nt.putBoolean('is_ds_attached', ds_attached)
316309

317-
self._update_feedback()
318-
self.robotPeriodic()
319-
delay.wait()
310+
try:
311+
self.disabledPeriodic()
312+
except:
313+
self.onException()
320314

321-
delay.free()
315+
self._update_feedback()
316+
self.robotPeriodic()
317+
delay.wait()
322318

323319
def operatorControl(self):
324320
"""
@@ -335,8 +331,6 @@ def operatorControl(self):
335331
self.__nt.putBoolean('is_ds_attached', self.ds.isDSAttached())
336332
wpilib.LiveWindow.setEnabled(False)
337333

338-
delay = NotifierDelay(self.control_loop_wait_time)
339-
340334
# initialize things
341335
self._on_mode_enable_components()
342336

@@ -345,20 +339,19 @@ def operatorControl(self):
345339
except:
346340
self.onException(forceReport=True)
347341

348-
while self.isOperatorControl() and self.isEnabled():
349-
350-
try:
351-
self.teleopPeriodic()
352-
except:
353-
self.onException()
342+
with NotifierDelay(self.control_loop_wait_time) as delay:
343+
while self.isOperatorControl() and self.isEnabled():
344+
try:
345+
self.teleopPeriodic()
346+
except:
347+
self.onException()
354348

355-
self._execute_components()
356-
self._update_feedback()
357-
self.robotPeriodic()
349+
self._execute_components()
350+
self._update_feedback()
351+
self.robotPeriodic()
358352

359-
delay.wait()
353+
delay.wait()
360354

361-
delay.free()
362355
self._on_mode_disable_components()
363356

364357
def test(self):
@@ -368,24 +361,21 @@ def test(self):
368361
self.__nt.putBoolean('is_ds_attached', self.ds.isDSAttached())
369362
wpilib.LiveWindow.setEnabled(True)
370363

371-
delay = NotifierDelay(self.control_loop_wait_time)
372-
373364
try:
374365
self.testInit()
375366
except:
376367
self.onException(forceReport=True)
377-
378-
while self.isTest() and self.isEnabled():
379-
try:
380-
self.testPeriodic()
381-
except:
382-
self.onException()
383368

384-
self._update_feedback()
385-
self.robotPeriodic()
386-
delay.wait()
369+
with NotifierDelay(self.control_loop_wait_time) as delay:
370+
while self.isTest() and self.isEnabled():
371+
try:
372+
self.testPeriodic()
373+
except:
374+
self.onException()
387375

388-
delay.free()
376+
self._update_feedback()
377+
self.robotPeriodic()
378+
delay.wait()
389379

390380
def _on_mode_enable_components(self):
391381
# initialize things

robotpy_ext/autonomous/selector.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -226,24 +226,22 @@ def run(self, control_loop_wait_time=0.020, iter_fn=None, on_exception=None):
226226
#
227227
# Autonomous control loop
228228
#
229-
230-
delay = NotifierDelay(control_loop_wait_time)
231-
232-
while self.ds.isAutonomous() and self.ds.isEnabled():
233-
234-
try:
235-
self._on_iteration(timer.get())
236-
except:
237-
on_exception()
238-
239-
if isinstance(iter_fn, (list, tuple)):
240-
for fn in iter_fn:
241-
fn()
242-
else:
243-
iter_fn()
244-
245-
delay.wait()
246-
229+
230+
with NotifierDelay(control_loop_wait_time) as delay:
231+
while self.ds.isAutonomous() and self.ds.isEnabled():
232+
try:
233+
self._on_iteration(timer.get())
234+
except:
235+
on_exception()
236+
237+
if isinstance(iter_fn, (list, tuple)):
238+
for fn in iter_fn:
239+
fn()
240+
else:
241+
iter_fn()
242+
243+
delay.wait()
244+
247245
#
248246
# Done with autonomous, finish up
249247
#

robotpy_ext/misc/precise_delay.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ class NotifierDelay:
8585
This will delay so that the next invocation of your loop happens at
8686
precisely the same period, assuming that your loop does not take longer
8787
than the specified period.
88+
89+
Example::
90+
91+
with NotifierDelay(0.02) as delay:
92+
while something:
93+
# do things here
94+
delay.wait()
8895
"""
8996

9097
def __init__(self, delay_period: float) -> None:
@@ -100,15 +107,26 @@ def __init__(self, delay_period: float) -> None:
100107

101108
wpilib.Resource._add_global_resource(self)
102109

110+
def __del__(self):
111+
self.free()
112+
113+
def __enter__(self) -> 'NotifierDelay':
114+
return self
115+
116+
def __exit__(self, exc_type, exc_val, exc_tb):
117+
self.free()
118+
103119
def free(self) -> None:
104120
"""Clean up the notifier.
105121
106-
Call this method once you are done using this object.
107122
Do not use this object after this method is called.
108123
"""
109124
handle = self._notifier
125+
if handle is None:
126+
return
110127
hal.stopNotifier(handle)
111128
hal.cleanNotifier(handle)
129+
self._notifier = None
112130

113131
def wait(self) -> None:
114132
"""Wait until the delay period has passed."""

0 commit comments

Comments
 (0)