Skip to content

Commit df9fe94

Browse files
authored
Merge pull request #103 from auscompgeek/notifier-delay
Add NotifierDelay
2 parents 417ba4e + 718c8d9 commit df9fe94

File tree

4 files changed

+112
-59
lines changed

4 files changed

+112
-59
lines changed

magicbot/magicrobot.py

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import wpilib
77

8-
from robotpy_ext.misc import PreciseDelay
8+
from robotpy_ext.misc import NotifierDelay
99
from robotpy_ext.autonomous import AutonomousModeSelector
1010

1111
from robotpy_ext.misc.orderedclass import OrderedClass
@@ -295,28 +295,26 @@ def disabled(self):
295295
ds_attached = None
296296
wpilib.LiveWindow.setEnabled(False)
297297

298-
delay = PreciseDelay(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()
314+
315+
self._update_feedback()
316+
self.robotPeriodic()
317+
delay.wait()
320318

321319
def operatorControl(self):
322320
"""
@@ -333,8 +331,6 @@ def operatorControl(self):
333331
self.__nt.putBoolean('is_ds_attached', self.ds.isDSAttached())
334332
wpilib.LiveWindow.setEnabled(False)
335333

336-
delay = PreciseDelay(self.control_loop_wait_time)
337-
338334
# initialize things
339335
self._on_mode_enable_components()
340336

@@ -343,18 +339,18 @@ def operatorControl(self):
343339
except:
344340
self.onException(forceReport=True)
345341

346-
while self.isOperatorControl() and self.isEnabled():
347-
348-
try:
349-
self.teleopPeriodic()
350-
except:
351-
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()
352348

353-
self._execute_components()
354-
self._update_feedback()
355-
self.robotPeriodic()
349+
self._execute_components()
350+
self._update_feedback()
351+
self.robotPeriodic()
356352

357-
delay.wait()
353+
delay.wait()
358354

359355
self._on_mode_disable_components()
360356

@@ -369,16 +365,17 @@ def test(self):
369365
self.testInit()
370366
except:
371367
self.onException(forceReport=True)
372-
373-
while self.isTest() and self.isEnabled():
374-
try:
375-
self.testPeriodic()
376-
except:
377-
self.onException()
378368

379-
self._update_feedback()
380-
self.robotPeriodic()
381-
wpilib.Timer.delay(self.control_loop_wait_time)
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()
375+
376+
self._update_feedback()
377+
self.robotPeriodic()
378+
delay.wait()
382379

383380
def _on_mode_enable_components(self):
384381
# initialize things

robotpy_ext/autonomous/selector.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import logging
88
logger = logging.getLogger('autonomous')
99

10-
from ..misc.precise_delay import PreciseDelay
10+
from ..misc.precise_delay import NotifierDelay
1111

1212
import wpilib
1313

@@ -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 = PreciseDelay(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
#
@@ -254,6 +252,7 @@ def run(self, control_loop_wait_time=0.020, iter_fn=None, on_exception=None):
254252
on_exception(forceReport=True)
255253

256254
logger.info("Autonomous mode ended")
255+
delay.free()
257256

258257
#
259258
# Internal methods used to implement autonomous mode switching, and

robotpy_ext/misc/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11

2-
from .precise_delay import PreciseDelay
2+
from .precise_delay import NotifierDelay, PreciseDelay
33
from .periodic_filter import PeriodicFilter

robotpy_ext/misc/precise_delay.py

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,62 @@ def _wait_unit_tests(self):
7777
# - TODO: should we just always use this in simulated mode?
7878

7979
wpilib.Timer.delay(self.delay_period)
80-
81-
80+
81+
82+
class NotifierDelay:
83+
"""Synchronizes a timing loop against interrupts from the FPGA.
84+
85+
This will delay so that the next invocation of your loop happens at
86+
precisely the same period, assuming that your loop does not take longer
87+
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()
95+
"""
96+
97+
def __init__(self, delay_period: float) -> None:
98+
""":param delay_period: The period's amount of time (in seconds)."""
99+
if delay_period < 0.001:
100+
raise ValueError("You probably don't want to delay less than 1ms!")
101+
102+
# Convert the delay period to microseconds, as FPGA timestamps are microseconds
103+
self.delay_period = int(delay_period * 1e6)
104+
self._notifier = hal.initializeNotifier()
105+
self._expiry_time = wpilib.RobotController.getFPGATime() + self.delay_period
106+
self._update_alarm()
107+
108+
wpilib.Resource._add_global_resource(self)
109+
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+
119+
def free(self) -> None:
120+
"""Clean up the notifier.
121+
122+
Do not use this object after this method is called.
123+
"""
124+
handle = self._notifier
125+
if handle is None:
126+
return
127+
hal.stopNotifier(handle)
128+
hal.cleanNotifier(handle)
129+
self._notifier = None
130+
131+
def wait(self) -> None:
132+
"""Wait until the delay period has passed."""
133+
hal.waitForNotifierAlarm(self._notifier)
134+
self._expiry_time += self.delay_period
135+
self._update_alarm()
136+
137+
def _update_alarm(self) -> None:
138+
hal.updateNotifierAlarm(self._notifier, self._expiry_time)

0 commit comments

Comments
 (0)