1
+ import traceback
2
+ from sys import argv
1
3
from typing import Any , Callable , Iterable , ClassVar
2
4
from heapq import heappush , heappop , _siftup
3
5
from hal import (
25
27
26
28
class _Callback :
27
29
28
- __slots__ = "func" , "_periodUs" , "expirationUs"
29
-
30
30
def __init__ (
31
31
self ,
32
32
func : Callable [[], None ],
@@ -100,25 +100,15 @@ def __repr__(self) -> str:
100
100
101
101
class _OrderedListSort :
102
102
103
- __slots__ = "_data"
104
-
105
103
def __init__ (self ) -> None :
106
104
self ._data : list [Any ] = []
107
105
108
106
def add (self , item : Any ) -> None :
109
107
self ._data .append (item )
110
108
self ._data .sort ()
111
109
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 ]
122
112
123
113
def reorderListAfterAChangeInTheFirstElement (self ):
124
114
self ._data .sort ()
@@ -138,24 +128,14 @@ def __repr__(self) -> str:
138
128
139
129
class _OrderedListMin :
140
130
141
- __slots__ = "_data"
142
-
143
131
def __init__ (self ) -> None :
144
132
self ._data : list [Any ] = []
145
133
146
134
def add (self , item : Any ) -> None :
147
135
self ._data .append (item )
148
136
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 )
159
139
160
140
def reorderListAfterAChangeInTheFirstElement (self ):
161
141
pass
@@ -175,24 +155,14 @@ def __repr__(self) -> str:
175
155
176
156
class _OrderedListHeapq :
177
157
178
- __slots__ = "_data"
179
-
180
158
def __init__ (self ) -> None :
181
159
self ._data : list [Any ] = []
182
160
183
161
def add (self , item : Any ) -> None :
184
162
heappush (self ._data , item )
185
163
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 ]
196
166
197
167
def reorderListAfterAChangeInTheFirstElement (self ):
198
168
_siftup (self ._data , 0 )
@@ -268,64 +238,72 @@ def startCompetition(self) -> None:
268
238
observeUserProgramStarting ()
269
239
270
240
# 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
+
323
246
finally :
247
+ print ("Reached after finally: self._stopNotifier(): " , flush = True )
324
248
# pytests hang on PC when we don't force a call to self._stopNotifier()
325
249
self ._stopNotifier ()
326
250
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
+
327
306
def _runCallbackAtHeadOfListAndReschedule (self , callback ) -> None :
328
- # callback = self._callbacks.peek()
329
307
# The callback.func() may have added more callbacks to self._callbacks,
330
308
# but each is sorted by the _getFPGATime() at the moment it is
331
309
# created, which is greater than self.expirationUs of this callback,
0 commit comments