Skip to content

Commit f37b198

Browse files
committed
It's broken
Signed-off-by: Mike Stitt <[email protected]>
1 parent 045aece commit f37b198

File tree

2 files changed

+111
-92
lines changed

2 files changed

+111
-92
lines changed

subprojects/robotpy-wpilib/tests/test_timedrobot.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,16 @@ def _robotThread(self, robot: TimedRobotPy) -> None:
101101
self._robotStarted = True
102102
self._cond.notify_all()
103103

104-
with self._reraise(catch=True):
104+
with self._reraise(catch=False): # todo put me back: self._reraise(catch=True):
105105
assert robot is not None # shouldn't happen...
106106

107107
robot._TestRobot__robotInitStarted = self._onRobotInitStarted
108108

109109
try:
110+
print("about to:robot.startCompetition()",flush=True)
110111
robot.startCompetition()
112+
print("after robot.startCompetition()",flush=True)
113+
print(f"reached self._expectFinished {self._expectFinished} == self._robotFinished {self._robotFinished}", flush=True)
111114
assert self._expectFinished == self._robotFinished
112115
finally:
113116
del robot
@@ -144,6 +147,7 @@ def runRobot(self) -> None:
144147
# in this block you should tell the sim to do sim things
145148
yield
146149
finally:
150+
print("Reached self._robotFinished", flush=True)
147151
self._robotFinished = True
148152
robot.endCompetition()
149153

subprojects/robotpy-wpilib/wpilib/timedrobotpy.py

Lines changed: 106 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import traceback
2+
from sys import argv
13
from typing import Any, Callable, Iterable, ClassVar
24
from heapq import heappush, heappop, _siftup
35
from hal import (
@@ -25,8 +27,6 @@
2527

2628
class _Callback:
2729

28-
__slots__ = "func", "_periodUs", "expirationUs"
29-
3030
def __init__(
3131
self,
3232
func: Callable[[], None],
@@ -100,21 +100,14 @@ def __repr__(self) -> str:
100100

101101
class _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
110+
def peek(self) -> Any:
118111
if self._data:
119112
return self._data[0]
120113
else:
@@ -138,20 +131,13 @@ def __repr__(self) -> str:
138131

139132
class _OrderedListMin:
140133

141-
__slots__ = "_data"
142-
143134
def __init__(self) -> None:
144135
self._data: list[Any] = []
145136

146137
def add(self, item: Any) -> None:
147138
self._data.append(item)
148139

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
140+
def peek(self) -> Any:
155141
if self._data:
156142
return min(self._data)
157143
else:
@@ -175,24 +161,14 @@ def __repr__(self) -> str:
175161

176162
class _OrderedListHeapq:
177163

178-
__slots__ = "_data"
179-
180164
def __init__(self) -> None:
181165
self._data: list[Any] = []
182166

183167
def add(self, item: Any) -> None:
184168
heappush(self._data, item)
185169

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
170+
def peek(self) -> Any:
171+
return self._data[0]
196172

197173
def reorderListAfterAChangeInTheFirstElement(self):
198174
_siftup(self._data, 0)
@@ -209,9 +185,23 @@ def __contains__(self, item) -> bool:
209185
def __repr__(self) -> str:
210186
return str(sorted(self._data))
211187

188+
# Hooks to use timeit to evaluate configurations of TimedRobotPy
189+
_OrderedList = _OrderedListSort
190+
_initializeNotifier = initializeNotifier
191+
_setNotifierName = setNotifierName
192+
_observeUserProgramStarting = observeUserProgramStarting
193+
_updateNotifierAlarm = updateNotifierAlarm
194+
_waitForNotifierAlarm = waitForNotifierAlarm
195+
_stopNotifier = stopNotifier
196+
_report = report
197+
_IterativeRobotPy = IterativeRobotPy
198+
if '_IterativeRobotPyIsObject' in argv:
199+
print("_IterativeRobotPyIsObject")
200+
_IterativeRobotPy = object
201+
212202

213203
# todo what should the name of this class be?
214-
class TimedRobotPy(IterativeRobotPy):
204+
class TimedRobotPy(_IterativeRobotPy):
215205
"""
216206
TimedRobotPy implements the IterativeRobotBase robot program framework.
217207
@@ -232,26 +222,30 @@ def __init__(self, period: wpimath.units.seconds = kDefaultPeriod) -> None:
232222
233223
:param period: period of the main robot periodic loop in seconds.
234224
"""
235-
super().__init__(period)
225+
if "_IterativeRobotPyIsObject" in argv:
226+
super().__init__()
227+
self._periodS = period
228+
else:
229+
super().__init__(period)
236230

237231
# All periodic functions created by addPeriodic are relative
238232
# to this self._startTimeUs
239233
self._startTimeUs = _getFPGATime()
240-
self._callbacks = _OrderedListSort()
234+
self._callbacks = _OrderedList()
241235
self._loopStartTimeUs = 0
242236
self.addPeriodic(self._loopFunc, period=self._periodS)
243237

244-
self._notifier, status = initializeNotifier()
238+
self._notifier, status = _initializeNotifier()
245239
if status != 0:
246240
raise RuntimeError(
247241
f"initializeNotifier() returned {self._notifier}, {status}"
248242
)
249243

250-
status = setNotifierName(self._notifier, "TimedRobotPy")
244+
status = _setNotifierName(self._notifier, "TimedRobotPy")
251245
if status != 0:
252246
raise RuntimeError(f"setNotifierName() returned {status}")
253247

254-
report(_kResourceType_Framework, _kFramework_Timed)
248+
_report(_kResourceType_Framework, _kFramework_Timed)
255249

256250
def startCompetition(self) -> None:
257251
"""
@@ -265,67 +259,88 @@ def startCompetition(self) -> None:
265259

266260
# Tell the DS that the robot is ready to be enabled
267261
print("********** Robot program startup complete **********", flush=True)
268-
observeUserProgramStarting()
262+
_observeUserProgramStarting()
269263

270264
# 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)
265+
# (really not forever, there is a check for a stop)
266+
while self._bodyOfMainLoop():
267+
pass
268+
print("Reached after while self._bodyOfMainLoop(): ", flush=True)
269+
270+
except Exception as e:
271+
# Print the exception type and message
272+
print(f"Exception caught: {type(e).__name__}: {e}")
273+
274+
# Print the stack trace
275+
print("Stack trace:")
276+
traceback.print_exc()
277+
278+
# Alternatively, get the formatted traceback as a string:
279+
# formatted_traceback = traceback.format_exc()
280+
# print(formatted_traceback)
281+
282+
# Rethrow the exception to propagate it up the call stack
283+
raise
284+
323285
finally:
286+
print("Reached after finally: self._stopNotifier(): ", flush=True)
324287
# pytests hang on PC when we don't force a call to self._stopNotifier()
325288
self._stopNotifier()
326289

290+
def _bodyOfMainLoop(self) -> bool:
291+
keepGoing = True
292+
# We don't have to check there's an element in the queue first because
293+
# there's always at least one (the constructor adds one).
294+
callback = self._callbacks.peek()
295+
296+
status = _updateNotifierAlarm(self._notifier, callback.expirationUs)
297+
if status != 0:
298+
raise RuntimeError(f"updateNotifierAlarm() returned {status}")
299+
300+
self._loopStartTimeUs, status = _waitForNotifierAlarm(self._notifier)
301+
302+
# The C++ code that this was based upon used the following line to establish
303+
# the loopStart time. Uncomment it and
304+
# the "self._loopStartTimeUs = startTimeUs" further below to emulate the
305+
# legacy behavior.
306+
# startTimeUs = _getFPGATime() # uncomment this for legacy behavior
307+
308+
if status != 0:
309+
raise RuntimeError(
310+
f"waitForNotifierAlarm() returned _loopStartTimeUs={self._loopStartTimeUs} status={status}"
311+
)
312+
313+
if self._loopStartTimeUs == 0:
314+
# when HAL_StopNotifier(self.notifier) is called the above waitForNotifierAlarm
315+
# will return a _loopStartTimeUs==0 and the API requires robots to stop any loops.
316+
# See the API for waitForNotifierAlarm
317+
keepGoing = False
318+
return keepGoing
319+
320+
# On a RoboRio 2, the following print statement results in values like:
321+
# print(f"expUs={callback.expirationUs} current={self._loopStartTimeUs}, legacy={startTimeUs}")
322+
# [2.27] expUs=3418017 current=3418078, legacy=3418152
323+
# [2.29] expUs=3438017 current=3438075, legacy=3438149
324+
# This indicates that there is about 60 microseconds of skid from
325+
# callback.expirationUs to self._loopStartTimeUs
326+
# and there is about 70 microseconds of skid from self._loopStartTimeUs to startTimeUs.
327+
# Consequently, this code uses "self._loopStartTimeUs, status = waitForNotifierAlarm"
328+
# to establish loopStartTime, rather than slowing down the code by adding an extra call to
329+
# "startTimeUs = _getFPGATime()".
330+
331+
# self._loopStartTimeUs = startTimeUs # Uncomment this line for legacy behavior.
332+
333+
self._runCallbackAtHeadOfListAndReschedule(callback)
334+
335+
# Process all other callbacks that are ready to run
336+
# Changing the comparison to be _getFPGATime() rather than
337+
# self._loopStartTimeUs would also be correct.
338+
while (
339+
callback := self._callbacks.peek()
340+
).expirationUs <= _getFPGATime():
341+
self._runCallbackAtHeadOfListAndReschedule(callback)
342+
327343
def _runCallbackAtHeadOfListAndReschedule(self, callback) -> None:
328-
# callback = self._callbacks.peek()
329344
# The callback.func() may have added more callbacks to self._callbacks,
330345
# but each is sorted by the _getFPGATime() at the moment it is
331346
# created, which is greater than self.expirationUs of this callback,
@@ -346,7 +361,7 @@ def _runCallbackAtHeadOfListAndReschedule(self, callback) -> None:
346361
self._callbacks.reorderListAfterAChangeInTheFirstElement()
347362

348363
def _stopNotifier(self):
349-
stopNotifier(self._notifier)
364+
_stopNotifier(self._notifier)
350365

351366
def endCompetition(self) -> None:
352367
"""

0 commit comments

Comments
 (0)