1"""tasking.py weightless thread scheduling 2 3 4""" 5#print("module {0}".format(__name__)) 6 7from ..aid.sixing import * 8from .globaling import * 9from ..aid.odicting import odict 10from . import excepting 11from . import registering 12from . import storing 13 14from ..aid.consoling import getConsole 15console = getConsole() 16 17 18def CreateInstances(store): 19 """Create server instances which automatically get registered on object creation 20 must be function so can recreate after clear registry 21 """ 22 tasker = Tasker(name = 'tasker', store = store) 23 24#Class definitions 25 26 27class Tasker(registering.StoriedRegistrar): 28 """Task class, Base class for weightless threads 29 30 """ 31 Counter = 0 32 Names = {} 33 34 def __init__(self, period = 0.0, schedule=INACTIVE, **kw): 35 """ 36 Initialize instance. 37 38 Inherited instance attributes 39 .name = unique name for tasker 40 .store = data store 41 42 Instance attributes 43 .period = desired time in seconds between runs,non negative, zero means asap 44 .stamp = depends on subclass default is time tasker last RUN 45 .status = operational status of tasker 46 .desire = desired control asked by this or other taskers 47 .done = tasker completion state True or False 48 .schedule = initial scheduling context for this tasker vis a vis skedder 49 .runner = generator to run tasker 50 51 The registry class will supply unique name when name is empty by using 52 the .__class__.__name__ as the default preface to the name. 53 To use a different default preface add this to the .__init__ method 54 before the super call 55 56 if 'preface' not in kw: 57 kw['preface'] = 'MyDefaultPreface' 58 59 """ 60 super(Tasker,self).__init__(**kw) 61 62 self.period = float(abs(period)) #desired time between runs, 0.0 means asap 63 self.stamp = 0.0 #time last run 64 self.presolved = False # tasker has been presolved 65 self.resolved = False # tasker has been resolved 66 self.status = STOPPED #operational status of tasker 67 self.desire = STOP #desired control next time Task is iterated 68 self.done = True # tasker completion state reset on restart 69 self.schedule = schedule #initial scheduling context vis a vis skedder 70 self.runner = None #reference to runner generator 71 self.remake() #make generator assign to .runner and advance to yield 72 73 def reinit(self, period=None, schedule=None, **kw): 74 if period is not None: 75 self.period = period 76 77 if schedule is not None: 78 self.schedule = schedule 79 80 def remake(self): 81 """Re make runner generator 82 83 .send(None) same as .next() 84 """ 85 self.runner = self.makeRunner() #make generator 86 status = self.runner.send(None) #advance to first yield to allow send(cmd) on next iteration 87 88 if console._verbosity >= console.Wordage.profuse: 89 self.expose() 90 91 def expose(self): 92 """ 93 94 """ 95 print(" Task %s status = %s" % (self.name, StatusNames[self.status])) 96 97 def presolve(self, **kwa): 98 """Presolves any links to aux clones""" 99 self.presolved = True 100 101 def resolve(self, **kwa): 102 """Resolves any by name links to other objects """ 103 self.resolved = True 104 105 def ready(self): 106 """ready runner 107 108 """ 109 return self.runner.send(READY) 110 111 def start(self): 112 """start runner 113 114 """ 115 return self.runner.send(START) 116 117 def run(self): 118 """run runner 119 120 """ 121 return self.runner.send(RUN) 122 123 def stop(self): 124 """stop runner 125 126 """ 127 return self.runner.send(STOP) 128 129 def abort(self): 130 """abort runner 131 132 """ 133 return self.runner.send(ABORT) 134 135 def makeRunner(self): 136 """generator factory function to create generator to run this tasker 137 138 Should be overridden in sub class 139 """ 140 #do any on creation initialization here 141 console.profuse(" Making Task Runner {0}\n".format(self.name)) 142 143 self.status = STOPPED #operational status of tasker 144 self.desire = STOP #default what to do next time, override below 145 self.done = True 146 147 count = 0 148 149 try: 150 while (True): 151 control = (yield (self.status)) #accept control and yield status 152 console.profuse("\n Iterate Tasker {0} with control = {1} status = {2}\n".format( 153 self.name, 154 ControlNames.get(control, 'Unknown'), 155 StatusNames.get(self.status, 'Unknown'))) 156 157 if control == RUN: 158 if self.status == STARTED or self.status == RUNNING: 159 console.profuse(" Running Tasker {0} ...\n".format(self.name)) 160 self.status = RUNNING 161 else: 162 console.profuse(" Need to Start Tasker {0}\n".format(self.name)) 163 self.desire = START 164 165 elif control == READY: 166 console.profuse(" Readying Tasker {0} ...\n".format(self.name)) 167 self.desire = START 168 self.status = READIED 169 170 elif control == START: 171 console.terse(" Starting Tasker {0} ...\n".format(self.name)) 172 self.desire = RUN 173 self.status = STARTED 174 self.done = False 175 176 elif control == STOP: 177 if self.status == RUNNING or self.status == STARTED: 178 console.terse(" Stopping Tasker {0} ...\n".format(self.name)) 179 self.desire = STOP 180 self.status = STOPPED 181 self.done = True 182 else: 183 console.terse(" Tasker {0} not started or running.\n".format(self.name)) 184 185 elif control == ABORT: 186 console.profuse(" Aborting Tasker {0} ...\n".format(self.name)) 187 self.desire = ABORT 188 self.status = ABORTED 189 self.done = True #only done if complete successfully 190 191 else: #control == unknown error condition bad control 192 self.desire = ABORT 193 self.status = ABORTED 194 console.profuse(" Aborting Tasker {0}, bad control = {1}\n".format( 195 self.name, CommandNames[control])) 196 break #break out of while loop. this will cause stopIteration 197 198 self.stamp = self.store.stamp 199 200 finally: #in case uncaught exception 201 console.profuse(" Exception causing Abort Tasker {0} ...\n".format(self.name)) 202 self.desire = ABORT 203 self.status = ABORTED 204 205 206def resolveTasker(tasker, who='', desc='tasker', contexts=None, human='', count=None): 207 """ Returns resolved tasker instance from tasker 208 tasker may be name of tasker or instance 209 who is optional name of object owning the link 210 such as framer or frame or actor 211 desc is string description of tasker link such as 'aux' or 'framer' 212 contexts is list of allowed schedule contexts, None or empty means any. 213 Taskers.Names registry must already be setup 214 """ 215 if not isinstance(tasker, Tasker): # not instance so name 216 if tasker not in Tasker.Names: 217 raise excepting.ResolveError("ResolveError: Bad {0} link name".format(desc), 218 tasker, 219 who, 220 human, 221 count) 222 tasker = Tasker.Names[tasker] 223 if contexts and tasker.schedule not in contexts: 224 raise excepting.ResolveError("ResolveError: Bad {0} link not scheduled" 225 " as one of {1}".format(desc, contexts), 226 tasker.name, 227 who, 228 human, 229 count) 230 console.concise(" Resolved {0} Tasker '{1}' in '{2}'\n" 231 "".format(desc, tasker.name, who)) 232 return tasker 233 234ResolveTasker = resolveTasker 235