1+ import traceback
2+ from sys import argv
13from typing import Any , Callable , Iterable , ClassVar
24from heapq import heappush , heappop , _siftup
35from hal import (
2527
2628class _Callback :
2729
28- __slots__ = "func" , "_periodUs" , "expirationUs"
29-
3030 def __init__ (
3131 self ,
3232 func : Callable [[], None ],
@@ -100,25 +100,15 @@ def __repr__(self) -> str:
100100
101101class _OrderedListSort :
102102
103- __slots__ = "_data"
104-
105103 def __init__ (self ) -> None :
106104 self ._data : list [Any ] = []
107105
108106 def add (self , item : Any ) -> None :
109107 self ._data .append (item )
110108 self ._data .sort ()
111109
112- def pop (self ) -> Any :
113- return self ._data .pop ()
114-
115- def peek (
116- self ,
117- ) -> Any : # todo change to Any | None when we don't build with python 3.9
118- if self ._data :
119- return self ._data [0 ]
120- else :
121- return None
110+ def peek (self ) -> Any :
111+ return self ._data [0 ]
122112
123113 def reorderListAfterAChangeInTheFirstElement (self ):
124114 self ._data .sort ()
@@ -138,24 +128,14 @@ def __repr__(self) -> str:
138128
139129class _OrderedListMin :
140130
141- __slots__ = "_data"
142-
143131 def __init__ (self ) -> None :
144132 self ._data : list [Any ] = []
145133
146134 def add (self , item : Any ) -> None :
147135 self ._data .append (item )
148136
149- # def pop(self) -> Any:
150- # return self._data.pop()
151-
152- def peek (
153- self ,
154- ) -> Any : # todo change to Any | None when we don't build with python 3.9
155- if self ._data :
156- return min (self ._data )
157- else :
158- return None
137+ def peek (self ) -> Any :
138+ return min (self ._data )
159139
160140 def reorderListAfterAChangeInTheFirstElement (self ):
161141 pass
@@ -175,24 +155,14 @@ def __repr__(self) -> str:
175155
176156class _OrderedListHeapq :
177157
178- __slots__ = "_data"
179-
180158 def __init__ (self ) -> None :
181159 self ._data : list [Any ] = []
182160
183161 def add (self , item : Any ) -> None :
184162 heappush (self ._data , item )
185163
186- def pop (self ) -> Any :
187- return heappop (self ._data )
188-
189- def peek (
190- self ,
191- ) -> Any : # todo change to Any | None when we don't build with python 3.9
192- if self ._data :
193- return self ._data [0 ]
194- else :
195- return None
164+ def peek (self ) -> Any :
165+ return self ._data [0 ]
196166
197167 def reorderListAfterAChangeInTheFirstElement (self ):
198168 _siftup (self ._data , 0 )
@@ -268,64 +238,72 @@ def startCompetition(self) -> None:
268238 observeUserProgramStarting ()
269239
270240 # Loop forever, calling the appropriate mode-dependent function
271- # (really not forever, there is a check for a break)
272- while True :
273- # We don't have to check there's an element in the queue first because
274- # there's always at least one (the constructor adds one). It's re-enqueued
275- # at the end of the loop.
276- callback = self ._callbacks .peek ()
277-
278- status = updateNotifierAlarm (self ._notifier , callback .expirationUs )
279- if status != 0 :
280- raise RuntimeError (f"updateNotifierAlarm() returned { status } " )
281-
282- self ._loopStartTimeUs , status = waitForNotifierAlarm (self ._notifier )
283-
284- # The C++ code that this was based upon used the following line to establish
285- # the loopStart time. Uncomment it and
286- # the "self._loopStartTimeUs = startTimeUs" further below to emulate the
287- # legacy behavior.
288- # startTimeUs = _getFPGATime() # uncomment this for legacy behavior
289-
290- if status != 0 :
291- raise RuntimeError (
292- f"waitForNotifierAlarm() returned _loopStartTimeUs={ self ._loopStartTimeUs } status={ status } "
293- )
294-
295- if self ._loopStartTimeUs == 0 :
296- # when HAL_StopNotifier(self.notifier) is called the above waitForNotifierAlarm
297- # will return a _loopStartTimeUs==0 and the API requires robots to stop any loops.
298- # See the API for waitForNotifierAlarm
299- break
300-
301- # On a RoboRio 2, the following print statement results in values like:
302- # print(f"expUs={callback.expirationUs} current={self._loopStartTimeUs}, legacy={startTimeUs}")
303- # [2.27] expUs=3418017 current=3418078, legacy=3418152
304- # [2.29] expUs=3438017 current=3438075, legacy=3438149
305- # This indicates that there is about 60 microseconds of skid from
306- # callback.expirationUs to self._loopStartTimeUs
307- # and there is about 70 microseconds of skid from self._loopStartTimeUs to startTimeUs.
308- # Consequently, this code uses "self._loopStartTimeUs, status = waitForNotifierAlarm"
309- # to establish loopStartTime, rather than slowing down the code by adding an extra call to
310- # "startTimeUs = _getFPGATime()".
311-
312- # self._loopStartTimeUs = startTimeUs # Uncomment this line for legacy behavior.
313-
314- self ._runCallbackAtHeadOfListAndReschedule (callback )
315-
316- # Process all other callbacks that are ready to run
317- # Changing the comparison to be _getFPGATime() rather than
318- # self._loopStartTimeUs would also be correct.
319- while (
320- callback := self ._callbacks .peek ()
321- ).expirationUs <= _getFPGATime ():
322- self ._runCallbackAtHeadOfListAndReschedule (callback )
241+ # (really not forever, there is a check for a stop)
242+ while self ._bodyOfMainLoop ():
243+ pass
244+ print ("Reached after while self._bodyOfMainLoop(): " , flush = True )
245+
323246 finally :
247+ print ("Reached after finally: self._stopNotifier(): " , flush = True )
324248 # pytests hang on PC when we don't force a call to self._stopNotifier()
325249 self ._stopNotifier ()
326250
251+ def _bodyOfMainLoop (self ) -> bool :
252+ keepGoing = True
253+ # We don't have to check there's an element in the queue first because
254+ # there's always at least one (the constructor adds one).
255+ callback = self ._callbacks .peek ()
256+
257+ status = updateNotifierAlarm (self ._notifier , callback .expirationUs )
258+ if status != 0 :
259+ raise RuntimeError (f"updateNotifierAlarm() returned { status } " )
260+
261+ self ._loopStartTimeUs , status = waitForNotifierAlarm (self ._notifier )
262+
263+ # The C++ code that this was based upon used the following line to establish
264+ # the loopStart time. Uncomment it and
265+ # the "self._loopStartTimeUs = startTimeUs" further below to emulate the
266+ # legacy behavior.
267+ # startTimeUs = _getFPGATime() # uncomment this for legacy behavior
268+
269+ if status != 0 :
270+ raise RuntimeError (
271+ f"waitForNotifierAlarm() returned _loopStartTimeUs={ self ._loopStartTimeUs } status={ status } "
272+ )
273+
274+ if self ._loopStartTimeUs == 0 :
275+ # when HAL_StopNotifier(self.notifier) is called the above waitForNotifierAlarm
276+ # will return a _loopStartTimeUs==0 and the API requires robots to stop any loops.
277+ # See the API for waitForNotifierAlarm
278+ keepGoing = False
279+ return keepGoing
280+
281+ # On a RoboRio 2, the following print statement results in values like:
282+ # print(f"expUs={callback.expirationUs} current={self._loopStartTimeUs}, legacy={startTimeUs}")
283+ # [2.27] expUs=3418017 current=3418078, legacy=3418152
284+ # [2.29] expUs=3438017 current=3438075, legacy=3438149
285+ # This indicates that there is about 60 microseconds of skid from
286+ # callback.expirationUs to self._loopStartTimeUs
287+ # and there is about 70 microseconds of skid from self._loopStartTimeUs to startTimeUs.
288+ # Consequently, this code uses "self._loopStartTimeUs, status = waitForNotifierAlarm"
289+ # to establish loopStartTime, rather than slowing down the code by adding an extra call to
290+ # "startTimeUs = _getFPGATime()".
291+
292+ # self._loopStartTimeUs = startTimeUs # Uncomment this line for legacy behavior.
293+
294+ self ._runCallbackAtHeadOfListAndReschedule (callback )
295+
296+ # Process all other callbacks that are ready to run
297+ # Changing the comparison to be _getFPGATime() rather than
298+ # self._loopStartTimeUs would also be correct.
299+ while (
300+ callback := self ._callbacks .peek ()
301+ ).expirationUs <= _getFPGATime ():
302+ self ._runCallbackAtHeadOfListAndReschedule (callback )
303+
304+ return keepGoing
305+
327306 def _runCallbackAtHeadOfListAndReschedule (self , callback ) -> None :
328- # callback = self._callbacks.peek()
329307 # The callback.func() may have added more callbacks to self._callbacks,
330308 # but each is sorted by the _getFPGATime() at the moment it is
331309 # created, which is greater than self.expirationUs of this callback,
0 commit comments