Skip to content

Commit 5895dcc

Browse files
committed
Only call endCompetition once during tests.
Signed-off-by: Mike Stitt <mike@stitt.cc>
1 parent 4cd8be6 commit 5895dcc

File tree

2 files changed

+53
-69
lines changed

2 files changed

+53
-69
lines changed

subprojects/robotpy-wpilib/tests/test_poc_timedrobot.py

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,6 @@
2525
from wpilib.simulation import pauseTiming, restartTiming
2626
import wpilib.simulation
2727

28-
29-
try:
30-
import commands2
31-
except ImportError:
32-
commands2 = None
33-
3428
from wpilib.timedrobotpy import TimedRobotPy
3529

3630
import contextlib
@@ -60,12 +54,12 @@ def __init__(self, reraise, robot: wpilib.RobotBase, expectFinished: bool):
6054

6155
self._cond = threading.Condition()
6256
self._robot_started = False
63-
self._robot_initialized = False
57+
self._robot_init_started = False
6458
self._robot_finished = False
6559

66-
def _on_robot_initialized(self):
60+
def _on_robot__init_started(self):
6761
with self._cond:
68-
self._robot_initialized = True
62+
self._robot_init_started = True
6963
self._cond.notify_all()
7064

7165
def _robot_thread(self, robot):
@@ -76,15 +70,12 @@ def _robot_thread(self, robot):
7670
with self._reraise(catch=True):
7771
assert robot is not None # shouldn't happen...
7872

79-
robot._TestRobot__robotInitialized = self._on_robot_initialized
73+
robot._TestRobot__robotInitStarted = self._on_robot__init_started
8074

8175
try:
8276
robot.startCompetition()
8377
assert self._expectFinished == self._robot_finished
8478
finally:
85-
# always call endCompetition or python hangs
86-
print("_robot_thread is calling endCompetition()")
87-
robot.endCompetition()
8879
del robot
8980

9081
@contextlib.contextmanager
@@ -113,14 +104,13 @@ def run_robot(self):
113104

114105
# If your robotInit is taking more than 2 seconds in simulation, you're
115106
# probably doing something wrong... but if not, please report a bug!
116-
assert self._cond.wait_for(lambda: self._robot_initialized, timeout=2)
107+
assert self._cond.wait_for(lambda: self._robot_init_started, timeout=2)
117108

118109
try:
119110
# in this block you should tell the sim to do sim things
120111
yield
121112
finally:
122113
self._robot_finished = True
123-
print("run_robot is calling endCompetition()")
124114
robot.endCompetition()
125115

126116
if isinstance(self._reraise.exception, RuntimeError):
@@ -147,7 +137,6 @@ def run_robot(self):
147137
if th.is_alive():
148138
pytest.fail("robot did not exit within 2 seconds")
149139

150-
self._robot = None
151140
self._thread = None
152141

153142
@property
@@ -214,10 +203,8 @@ def decorated_robot_class(myrobot_class) -> tuple:
214203
# to do that
215204
class TestRobot(robotClass):
216205
def robotInit(self):
217-
try:
218-
super().robotInit()
219-
finally:
220-
self.__robotInitialized()
206+
self.__robotInitStarted()
207+
super().robotInit()
221208

222209
TestRobot.__name__ = robotClass.__name__
223210
TestRobot.__module__ = robotClass.__module__
@@ -263,21 +250,11 @@ def robot_with_sim_setup_teardown(decorated_robot_class):
263250
# Tests only get a proxy to ensure cleanup is more reliable
264251
yield weakref.proxy(robot)
265252

266-
# If running in separate processes, no need to do cleanup
267-
# if ISOLATED:
268-
# # .. and funny enough, in isolated mode we *don't* want the
269-
# # robot to be cleaned up, as that can deadlock
270-
# self._saved_robot = robot
271-
# return
272-
273253
# HACK: avoid motor safety deadlock
274254
wpilib.simulation._simulation._resetMotorSafety()
275255

276256
del robot
277257

278-
if commands2 is not None:
279-
commands2.CommandScheduler.resetInstance()
280-
281258
# Double-check all objects are destroyed so that HAL handles are released
282259
gc.collect()
283260

subprojects/robotpy-wpilib/wpilib/timedrobotpy.py

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def __init__(self, period: wpimath.units.seconds = kDefaultPeriod) -> None:
155155
self.addPeriodic(self._loopFunc, period=self._periodS)
156156

157157
self._notifier, status = initializeNotifier()
158+
print(f"{self._notifier}, {status} = initializeNotifier()")
158159
if status != 0:
159160
raise RuntimeError(
160161
f"initializeNotifier() returned {self._notifier}, {status}"
@@ -170,59 +171,65 @@ def startCompetition(self) -> None:
170171
"""
171172
Provide an alternate "main loop" via startCompetition().
172173
"""
173-
self.robotInit()
174-
175-
if self.isSimulation():
176-
self._simulationInit()
177-
178-
# Tell the DS that the robot is ready to be enabled
179-
print("********** Robot program startup complete **********")
180-
observeUserProgramStarting()
181-
182-
# Loop forever, calling the appropriate mode-dependent function
183-
# (really not forever, there is a check for a break)
184-
while True:
185-
# We don't have to check there's an element in the queue first because
186-
# there's always at least one (the constructor adds one). It's re-enqueued
187-
# at the end of the loop.
188-
callback = self._callbacks.pop()
189-
190-
status = updateNotifierAlarm(self._notifier, callback.expirationUs)
191-
if status != 0:
192-
raise RuntimeError(f"updateNotifierAlarm() returned {status}")
193-
194-
currentTimeUs, status = waitForNotifierAlarm(self._notifier)
195-
if status != 0:
196-
raise RuntimeError(
197-
f"waitForNotifierAlarm() returned currentTimeUs={currentTimeUs} status={status}"
198-
)
199-
200-
if currentTimeUs == 0:
201-
# when HAL_StopNotifier(self.notifier) is called the above waitForNotifierAlarm
202-
# will return a currentTimeUs==0 and the API requires robots to stop any loops.
203-
# See the API for waitForNotifierAlarm
204-
break
205-
206-
self._loopStartTimeUs = _getFPGATime()
207-
self._runCallbackAndReschedule(callback, currentTimeUs)
208-
209-
# Process all other callbacks that are ready to run
210-
while self._callbacks.peek().expirationUs <= currentTimeUs:
174+
try:
175+
self.robotInit()
176+
177+
if self.isSimulation():
178+
self._simulationInit()
179+
180+
# Tell the DS that the robot is ready to be enabled
181+
print("********** Robot program startup complete **********", flush=True)
182+
observeUserProgramStarting()
183+
184+
# Loop forever, calling the appropriate mode-dependent function
185+
# (really not forever, there is a check for a break)
186+
while True:
187+
# We don't have to check there's an element in the queue first because
188+
# there's always at least one (the constructor adds one). It's re-enqueued
189+
# at the end of the loop.
211190
callback = self._callbacks.pop()
191+
192+
status = updateNotifierAlarm(self._notifier, callback.expirationUs)
193+
if status != 0:
194+
raise RuntimeError(f"updateNotifierAlarm() returned {status}")
195+
196+
currentTimeUs, status = waitForNotifierAlarm(self._notifier)
197+
if status != 0:
198+
raise RuntimeError(
199+
f"waitForNotifierAlarm() returned currentTimeUs={currentTimeUs} status={status}"
200+
)
201+
202+
if currentTimeUs == 0:
203+
# when HAL_StopNotifier(self.notifier) is called the above waitForNotifierAlarm
204+
# will return a currentTimeUs==0 and the API requires robots to stop any loops.
205+
# See the API for waitForNotifierAlarm
206+
break
207+
208+
self._loopStartTimeUs = _getFPGATime()
212209
self._runCallbackAndReschedule(callback, currentTimeUs)
213210

211+
# Process all other callbacks that are ready to run
212+
while self._callbacks.peek().expirationUs <= currentTimeUs:
213+
callback = self._callbacks.pop()
214+
self._runCallbackAndReschedule(callback, currentTimeUs)
215+
finally:
216+
self._stopNotifier()
217+
214218
def _runCallbackAndReschedule(
215219
self, callback: _Callback, currentTimeUs: microsecondsAsInt
216220
) -> None:
217221
callback.func()
218222
callback.setNextStartTimeUs(currentTimeUs)
219223
self._callbacks.add(callback)
220224

225+
def _stopNotifier(self):
226+
stopNotifier(self._notifier)
227+
221228
def endCompetition(self) -> None:
222229
"""
223230
Ends the main loop in startCompetition().
224231
"""
225-
stopNotifier(self._notifier)
232+
self._stopNotifier()
226233

227234
def getLoopStartTime(self) -> microsecondsAsInt:
228235
"""

0 commit comments

Comments
 (0)