88from wpilib .iterativerobotpy import IterativeRobotPy
99
1010
11+ def calcFutureExpirationUs (currentTimeUs : int , startTimeUs : int , offsetUs :int , periodUs :int ) -> float :
12+ # increment the expiration time by the number of full periods it's behind
13+ # plus one to avoid rapid repeat fires from a large loop overrun. We assume
14+ # currentTime ≥ startTimeUs rather than checking for it since the
15+ # callback wouldn't be running otherwise.
16+ # todo does this math work?
17+ # todo does the "// periodUs * periodUs" do the correct integer math?
18+ return startTimeUs + offsetUs + periodUs + \
19+ ((currentTimeUs - startTimeUs ) // periodUs ) * periodUs
20+
21+
1122class Callback :
1223 def __init__ (self , func , periodUs : int , expirationUs : int ):
1324 self .func = func
@@ -20,27 +31,17 @@ def makeCallBack(cls,
2031 startTimeUs : int ,
2132 periodUs : int ,
2233 offsetUs : int ):
23- # todo does the "// periodUs * periodUs" do the correct integer math?
24- nowUs = RobotController .getFPGATime ()
25- #print(f"makeCallBack getFPGATim={nowUs}")
26- expirationUs = \
27- startTimeUs + offsetUs + periodUs + \
28- ((nowUs - startTimeUs ) // periodUs ) * periodUs
34+ currentTimeUs = RobotController .getFPGATime ()
35+ expirationUs = calcFutureExpirationUs (currentTimeUs , startTimeUs , offsetUs , periodUs )
2936
3037 return Callback (
3138 func ,
3239 periodUs ,
3340 expirationUs
3441 )
3542
36- def setNextExpirationTimeDelta (self , currentTimeUs : int ):
37- # increment the expiration time by the number of full periods it's behind
38- # plus one to avoid rapid repeat fires from a large loop overrun. We assume
39- # currentTime ≥ expirationTime rather than checking for it since the
40- # callback wouldn't be running otherwise.
41- # todo does this math work?
42- self .expirationUs = self .expirationUs + self .periodUs \
43- + ((currentTimeUs - self .expirationUs ) // self .periodUs ) * self .periodUs
43+ def setNextStartTimeUs (self , currentTimeUs : int ):
44+ self .expirationUs = calcFutureExpirationUs (currentTimeUs , startTimeUs = self .expirationUs , offsetUs = 0 , periodUs = self .periodUs )
4445
4546 def __lt__ (self , other ):
4647 return self .expirationUs < other .expirationUs
@@ -80,23 +81,22 @@ def __str__(self):
8081
8182class TimedRobotPy (IterativeRobotPy ):
8283
83- def __init__ (self , periodS : float = 0.020 ): # todo are the units on period correct?
84+ def __init__ (self , periodS : float = 0.020 ):
8485 super ().__init__ (periodS )
8586
8687 self .startTimeUs = RobotController .getFPGATime ()
87- #print(f"self.startTimeUs={int(self.startTimeUs/timedelta(microseconds=1))}")
8888 self .callbacks = OrderedList ()
8989 self .addPeriodic (self .loopFunc , period = periodS )
9090
9191 self .notifier , status = initializeNotifier ()
92- #print(f"Initialized status={status} notifier={self.notifier}", flush=True)
93- if status != 1 :
92+ if status != 0 :
9493 message = f"initializeNotifier() returned { status } { self .notifier } "
95- raise RuntimeError (message )
94+ # raise RuntimeError(message) # todo
9695
9796 status = setNotifierName (self .notifier , "TimedRobot" )
98- if status != 1 :
99- raise RuntimeError (f"setNotifierName() returned { status } " )
97+ if status != 0 :
98+ message = f"setNotifierName() returned { status } "
99+ #raise RuntimeError(message) # todo
100100
101101 report (hal .tResourceType .kResourceType_Framework , hal .tInstances .kFramework_Timed )
102102
@@ -118,38 +118,33 @@ def startCompetition(self) -> None:
118118 # at the end of the loop.
119119 callback = self .callbacks .pop ()
120120
121- #print(f"type(callback)={type(callback)} type(callback.expirationUs)={type(callback.expirationUs)} callback.expirationUs={callback.expirationUs}", flush=True)
122-
123- timeout = callback .expirationUs
124- fpgaTime = RobotController .getFPGATime ()
125- #print(f"timeout={timeout} fpgatime={fpgaTime}", flush=True)
126- status = updateNotifierAlarm (self .notifier , timeout )
127- #print(f"updateNotifierAlarm({self.notifier}, {timeout}) status={status}", flush=True)
128- #if status != 0:
129- # message = f"updateNotifierAlarm() returned {status}"
130- # print(message, flush=True)
131- # raise RuntimeError(message)
121+ status = updateNotifierAlarm (self .notifier , callback .expirationUs )
122+ if status != 0 :
123+ message = f"updateNotifierAlarm() returned { status } "
124+ #raise RuntimeError(message) # todo
132125
133- currentTimeUs , status = waitForNotifierAlarm (self .notifier ) # todo what are the return values, what is the return order?
126+ currentTimeUs , status = waitForNotifierAlarm (self .notifier )
134127 if status != 0 :
135128 message = f"waitForNotifierAlarm() returned currentTimeUs={ currentTimeUs } status={ status } "
136- #print(message, flush=True)
137- #raise RuntimeError(message)
129+ #raise RuntimeError(message) # todo
138130
139131 if currentTimeUs == 0 :
132+ # when HAL_StopNotifier(self.notifier) is called the above waitForNotifierAlarm
133+ # will return a currentTimeUs==0 and the API requires robots to stop any loops.
134+ # See the api for waitForNotifierAlarm
140135 break
136+
141137 self .loopStartTimeUs = RobotController .getFPGATime ()
142- #print(f"self.loopStartTimeUs={self.loopStartTimeUs}")
143138 self ._runCallbackAndReschedule (callback , currentTimeUs )
144139
145140 # Process all other callbacks that are ready to run
146141 while self .callbacks .peek ().expirationUs <= currentTimeUs :
147142 callback = self .callbacks .pop ()
148143 self ._runCallbackAndReschedule (callback , currentTimeUs )
149144
150- def _runCallbackAndReschedule (self , callback , currentTimeUs ):
145+ def _runCallbackAndReschedule (self , callback , currentTimeUs : int ):
151146 callback .func ()
152- callback .setNextExpirationTimeDelta (currentTimeUs )
147+ callback .setNextStartTimeUs (currentTimeUs )
153148 self .callbacks .add (callback )
154149
155150 def endCompetition (self ):
0 commit comments