33import datetime
44import sys
55
6- # Exception used to stop a coroutine:
76class StopCoroutineException ( Exception ):
7+ '''Exception used to stop a coroutine:'''
88 pass
99
1010ProgramStartTime = datetime .datetime .now ()
1111
12+
1213class Scheduler ():
14+ ''' This manages an arbitrary number of coroutines (implemented as generator functions), supporting
15+ invoking each every *timeMillisBetweenWorkCalls*, and detecting when each has completed.
1316
14- # Public members:
15- # latestSensorCoroutine - the most recent sensor/control coroutine to have been added.
16- # latestActionCoroutine - the most recent motor control coroutine to have been added.
17+ It supports one special coroutine - the updatorCoroutine, which is invoked before and after all the other ones.
18+ '''
1719
18- # Answers the time in ms since program start.
1920 @staticmethod
2021 def currentTimeMillis ():
22+ 'Answers the time in floating point milliseconds since program start.'
2123 global ProgramStartTime
2224 c = datetime .datetime .now () - ProgramStartTime
2325 return c .days * (3600.0 * 1000 * 24 ) + c .seconds * 1000.0 + c .microseconds / 1000.0
2426
25- # Noop coroutine - does nothing
27+
2628 @staticmethod
2729 def nullCoroutine ():
30+ # Noop coroutine - does nothing
2831 while True :
2932 yield
3033
@@ -34,8 +37,9 @@ def __init__(self, timeMillisBetweenWorkCalls = 20):
3437 self .timeOfLastCall = Scheduler .currentTimeMillis ()
3538 self .updateCoroutine = self .nullCoroutine () # for testing - usually replaced.
3639
37- # Private: Executes all the coroutines, handling exceptions.
3840 def doWork (self ):
41+ # Private: Executes all the coroutines, handling exceptions.
42+
3943 timeNow = Scheduler .currentTimeMillis ()
4044 if timeNow == self .timeOfLastCall : # Ensure each call gets a different timer value.
4145 return
@@ -52,51 +56,49 @@ def doWork(self):
5256
5357 self .updateCoroutine .next ()
5458
55- # Private: Wait time before the next doWork call should be called.
5659 def timeMillisToNextCall (self ):
60+ # Private: Wait time before the next doWork call should be called.
5761 timeRequired = self .timeMillisBetweenWorkCalls + self .timeOfLastCall - Scheduler .currentTimeMillis ()
5862 return max ( timeRequired , 0 )
5963
60- # Add one or more new sensor/program coroutines to be scheduled, answering the last one.
64+
6165 def addSensorCoroutine (self , * coroutineList ):
66+ 'Add one or more new sensor/program coroutines to be scheduled, answering the last one to be added.'
6267 self .coroutines [0 :0 ] = coroutineList
63- self .latestSensorCoroutine = coroutineList [- 1 ]
64- return self .latestSensorCoroutine
68+ return coroutineList [- 1 ]
6569
66- # Add one or more new motor control coroutines to be scheduled, answering the last coroutine to be added.
6770 def addActionCoroutine (self , * coroutineList ):
71+ 'Add one or more new motor control coroutines to be scheduled, answering the last coroutine to be added.'
6872 self .coroutines .extend ( coroutineList )
69- self .latestActionCoroutine = coroutineList [- 1 ]
70- return self .latestActionCoroutine
73+ return coroutineList [- 1 ]
7174
72- # Private - set the coroutine that manages the interaction with the BrickPi.
73- # This will be invoked once at the start of each doWork call, and then again at the end.
7475 def addUpdateCoroutine (self , coroutine ):
76+ # Private - set the coroutine that manages the interaction with the BrickPi.
77+ # The coroutine will be invoked once at the start and once at the end of each doWork call.
7578 self .updateCoroutine = coroutine
7679
77- # Terminate the given one or more coroutines
7880 def stopCoroutine ( self , * coroutineList ):
81+ 'Terminate the given one or more coroutines'
7982 for coroutine in coroutineList :
8083 try :
8184 coroutine .throw (StopCoroutineException )
8285 except (StopCoroutineException ,StopIteration ): # If the coroutine doesn't catch the exception to tidy up, it comes back here.
8386 self .coroutines .remove ( coroutine )
8487
85- # Terminate all coroutines - use with care, of course!
8688 def stopAllCoroutines (self ):
89+ 'Terminate all coroutines (except the updater one) - use with care, of course!'
8790 self .stopCoroutine (* self .coroutines [:]) # Makes a copy of the list - don't want to be changing it.
8891
89- # Number of active coroutines
9092 def numCoroutines ( self ):
93+ 'Answers the number of active coroutines'
9194 return len (self .coroutines )
9295
93- # Answers whether any of the given coroutines are still executing
9496 def stillRunning ( self , * coroutineList ):
97+ 'Answers whether any of the given coroutines are still executing'
9598 return any ( c in self .coroutines for c in coroutineList )
9699
97-
98- # Coroutine that executes the given coroutines until the first completes, then stops the others and finishes.
99100 def runTillFirstCompletes ( self , * coroutineList ):
101+ 'Coroutine that executes the given coroutines until the first completes, then stops the others and finishes.'
100102 while True :
101103 for coroutine in coroutineList :
102104 try :
@@ -108,8 +110,8 @@ def runTillFirstCompletes( self, *coroutineList ):
108110 return
109111 yield
110112
111- # Coroutine that executes the given coroutines until all have completed.
112113 def runTillAllComplete (self , * coroutineList ):
114+ 'Coroutine that executes the given coroutines until all have completed.'
113115 coroutines = list ( coroutineList )
114116 while coroutines != []:
115117 for coroutine in coroutines :
@@ -122,17 +124,17 @@ def runTillAllComplete(self, *coroutineList ):
122124 return
123125 yield
124126
125- # Coroutine that waits for timeMillis, then finishes.
126127 def doWait ( self , timeMillis ):
128+ 'Coroutine that waits for timeMillis, then finishes.'
127129 t = Scheduler .currentTimeMillis ()
128130 while Scheduler .currentTimeMillis () - t < timeMillis :
129131 yield
130132
131- # Coroutine that wraps the given coroutine with a timeout:
132133 def withTimeout ( self , timeoutMillis , coroutine ):
134+ 'Coroutine that wraps the given coroutine with a timeout'
133135 return self .runTillFirstCompletes ( coroutine , self .doWait ( timeoutMillis ) )
134136
135- # Coroutine that waits until the given function (with optional parameters) returns True.
136137 def waitFor (self , function , * args ):
138+ 'Coroutine that waits until the given function (with optional parameters) returns True.'
137139 while not function (* args ):
138140 yield
0 commit comments