8
8
from wpilib .iterativerobotpy import IterativeRobotPy
9
9
10
10
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
+
11
22
class Callback :
12
23
def __init__ (self , func , periodUs : int , expirationUs : int ):
13
24
self .func = func
@@ -20,27 +31,17 @@ def makeCallBack(cls,
20
31
startTimeUs : int ,
21
32
periodUs : int ,
22
33
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 )
29
36
30
37
return Callback (
31
38
func ,
32
39
periodUs ,
33
40
expirationUs
34
41
)
35
42
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 )
44
45
45
46
def __lt__ (self , other ):
46
47
return self .expirationUs < other .expirationUs
@@ -80,23 +81,22 @@ def __str__(self):
80
81
81
82
class TimedRobotPy (IterativeRobotPy ):
82
83
83
- def __init__ (self , periodS : float = 0.020 ): # todo are the units on period correct?
84
+ def __init__ (self , periodS : float = 0.020 ):
84
85
super ().__init__ (periodS )
85
86
86
87
self .startTimeUs = RobotController .getFPGATime ()
87
- #print(f"self.startTimeUs={int(self.startTimeUs/timedelta(microseconds=1))}")
88
88
self .callbacks = OrderedList ()
89
89
self .addPeriodic (self .loopFunc , period = periodS )
90
90
91
91
self .notifier , status = initializeNotifier ()
92
- #print(f"Initialized status={status} notifier={self.notifier}", flush=True)
93
- if status != 1 :
92
+ if status != 0 :
94
93
message = f"initializeNotifier() returned { status } { self .notifier } "
95
- raise RuntimeError (message )
94
+ # raise RuntimeError(message) # todo
96
95
97
96
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
100
100
101
101
report (hal .tResourceType .kResourceType_Framework , hal .tInstances .kFramework_Timed )
102
102
@@ -118,38 +118,33 @@ def startCompetition(self) -> None:
118
118
# at the end of the loop.
119
119
callback = self .callbacks .pop ()
120
120
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
132
125
133
- currentTimeUs , status = waitForNotifierAlarm (self .notifier ) # todo what are the return values, what is the return order?
126
+ currentTimeUs , status = waitForNotifierAlarm (self .notifier )
134
127
if status != 0 :
135
128
message = f"waitForNotifierAlarm() returned currentTimeUs={ currentTimeUs } status={ status } "
136
- #print(message, flush=True)
137
- #raise RuntimeError(message)
129
+ #raise RuntimeError(message) # todo
138
130
139
131
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
140
135
break
136
+
141
137
self .loopStartTimeUs = RobotController .getFPGATime ()
142
- #print(f"self.loopStartTimeUs={self.loopStartTimeUs}")
143
138
self ._runCallbackAndReschedule (callback , currentTimeUs )
144
139
145
140
# Process all other callbacks that are ready to run
146
141
while self .callbacks .peek ().expirationUs <= currentTimeUs :
147
142
callback = self .callbacks .pop ()
148
143
self ._runCallbackAndReschedule (callback , currentTimeUs )
149
144
150
- def _runCallbackAndReschedule (self , callback , currentTimeUs ):
145
+ def _runCallbackAndReschedule (self , callback , currentTimeUs : int ):
151
146
callback .func ()
152
- callback .setNextExpirationTimeDelta (currentTimeUs )
147
+ callback .setNextStartTimeUs (currentTimeUs )
153
148
self .callbacks .add (callback )
154
149
155
150
def endCompetition (self ):
0 commit comments