00001 
00002 
00003 """
00004     pi_trees_lib.py - Version 0.1 2013-08-28
00005     
00006     Core classes for implementing Behavior Trees in Python
00007     
00008     Created for the Pi Robot Project: http://www.pirobot.org
00009     Copyright (c) 2013 Patrick Goebel.  All rights reserved.
00010 
00011     This program is free software; you can redistribute it and/or modify
00012     it under the terms of the GNU General Public License as published by
00013     the Free Software Foundation; either version 2 of the License, or
00014     (at your option) any later version.
00015     
00016     This program is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019     GNU General Public License for more details at:
00020     
00021     http://www.gnu.org/licenses/gpl.html
00022 """
00023 
00024 import string
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 class TaskStatus(object):
00034     """ A class for enumerating task statuses """
00035     FAILURE = 0
00036     SUCCESS = 1
00037     RUNNING = 2
00038     
00039 class Task(object):
00040     """ "The base Task class """
00041     def __init__(self, name, children=None, *args, **kwargs):
00042         self.name = name
00043         self.status = None
00044                 
00045         if children is None:
00046             children = []
00047             
00048         self.children = children
00049                          
00050     def run(self):
00051         pass
00052 
00053     def reset(self):
00054         for c in self.children:
00055             c.reset()
00056 
00057     def add_child(self, c):
00058         self.children.append(c)
00059 
00060     def remove_child(self, c):
00061         self.children.remove(c)
00062         
00063     def prepend_child(self, c):
00064         self.children.insert(0, c)
00065         
00066     def insert_child(self, c, i):
00067         self.children.insert(i, c)
00068         
00069     def get_status(self):
00070         return self.status
00071     
00072     def set_status(self, s):
00073         self.status = s
00074     
00075     def announce(self):
00076         print("Executing task " + str(self.name))
00077     
00078     
00079     def __enter__(self):
00080         return self.name
00081     
00082     def __exit__(self, exc_type, exc_val, exc_tb):
00083         if  exc_type is not None:
00084             return False
00085         return True
00086 
00087 class Selector(Task):
00088     """ A selector runs each task in order until one succeeds,
00089         at which point it returns SUCCESS. If all tasks fail, a FAILURE
00090         status is returned.  If a subtask is still RUNNING, then a RUNNING
00091         status is returned and processing continues until either SUCCESS
00092         or FAILURE is returned from the subtask.
00093     """
00094     def __init__(self, name, *args, **kwargs):
00095         super(Selector, self).__init__(name, *args, **kwargs)
00096  
00097     def run(self):
00098         for c in self.children:
00099             
00100             c.status = c.run()
00101             
00102             if c.status != TaskStatus.FAILURE:
00103                 return c.status
00104 
00105         return TaskStatus.FAILURE
00106  
00107 class Sequence(Task):
00108     """
00109         A sequence runs each task in order until one fails,
00110         at which point it returns FAILURE. If all tasks succeed, a SUCCESS
00111         status is returned.  If a subtask is still RUNNING, then a RUNNING
00112         status is returned and processing continues until either SUCCESS
00113         or FAILURE is returned from the subtask.
00114     """
00115     def __init__(self, name, *args, **kwargs):
00116         super(Sequence, self).__init__(name, *args, **kwargs)
00117  
00118     def run(self):
00119         for c in self.children:
00120             
00121             c.status = c.run()
00122                          
00123             if c.status != TaskStatus.SUCCESS:
00124                 return c.status   
00125             
00126         return TaskStatus.SUCCESS
00127     
00128 class Iterator(Task):
00129     """
00130         Iterate through all child tasks ignoring failure.
00131     """
00132     def __init__(self, name, *args, **kwargs):
00133         super(Iterator, self).__init__(name, *args, **kwargs)
00134  
00135     def run(self):
00136         for c in self.children:
00137             
00138             c.status = c.run()
00139                          
00140             if c.status != TaskStatus.SUCCESS and c.status != TaskStatus.FAILURE:
00141                 return c.status
00142             
00143         return TaskStatus.SUCCESS
00144     
00145 class ParallelOne(Task):
00146     """
00147         A parallel task runs each child task at (roughly) the same time.
00148         The ParallelOne task returns success as soon as any child succeeds.
00149     """
00150     def __init__(self, name, *args, **kwargs):
00151         super(ParallelOne, self).__init__(name, *args, **kwargs)
00152                  
00153     def run(self):
00154         for c in self.children:
00155             c.status = c.run()
00156 
00157             if c.status == TaskStatus.SUCCESS:
00158                 return TaskStatus.SUCCESS
00159         
00160         return TaskStatus.FAILURE
00161         
00162 class ParallelAll(Task):
00163     """
00164         A parallel task runs each child task at (roughly) the same time.
00165         The ParallelAll task requires all subtasks to succeed for it to succeed.
00166     """
00167     def __init__(self, name, *args, **kwargs):
00168         super(ParallelAll, self).__init__(name, *args, **kwargs)
00169                  
00170     def run(self):
00171         n_success = 0
00172         n_children = len(self.children)
00173 
00174         for c in self.children:
00175             c.status = c.run()
00176             if c.status == TaskStatus.SUCCESS:
00177                 n_success += 1
00178 
00179             if c.status == TaskStatus.FAILURE:
00180                 return TaskStatus.FAILURE
00181 
00182         if n_success == n_children:
00183             return TaskStatus.SUCCESS
00184         else:
00185             return TaskStatus.RUNNING
00186         
00187 class Loop(Task):
00188     """
00189         Loop over one or more subtasks for the given number of iterations
00190         Use the value -1 to indicate a continual loop.
00191     """
00192     def __init__(self, name, announce=True, *args, **kwargs):
00193         super(Loop, self).__init__(name, *args, **kwargs)
00194         
00195         self.iterations = kwargs['iterations']
00196         self.announce = announce
00197         self.loop_count = 0
00198         self.name = name
00199         print("Loop iterations: " + str(self.iterations))
00200         
00201     def run(self):
00202         
00203         while True:
00204             if self.iterations != -1 and self.loop_count >= self.iterations:
00205                 return TaskStatus.SUCCESS
00206                         
00207             for c in self.children:
00208                 while True:
00209                     c.status = c.run()
00210                     
00211                     if c.status == TaskStatus.SUCCESS:
00212                         break
00213 
00214                     return c.status
00215                 
00216                 c.reset()
00217                 
00218             self.loop_count += 1
00219             
00220             if self.announce:
00221                 print(self.name + " COMPLETED " + str(self.loop_count) + " LOOP(S)")
00222 
00223 
00224 class IgnoreFailure(Task):
00225     """
00226         Always return either RUNNING or SUCCESS.
00227     """
00228     def __init__(self, name, *args, **kwargs):
00229         super(IgnoreFailure, self).__init__(name, *args, **kwargs)
00230  
00231     def run(self):
00232         
00233         for c in self.children:
00234             
00235             c.status = c.run()
00236             
00237             if c.status == TaskStatus.FAILURE:
00238                 return TaskStatus.SUCCESS
00239             else:
00240                 return c.status
00241 
00242         return TaskStatus.SUCCESS
00243     
00244 class TaskNot(Task):
00245     """
00246         Turn SUCCESS into FAILURE and vice-versa
00247     """
00248     def __init__(self, name, *args, **kwargs):
00249         super(TaskNot, self).__init__(name, *args, **kwargs)
00250  
00251     def run(self):
00252         
00253         for c in self.children:
00254             
00255             c.status = c.run()
00256             
00257             if c.status == TaskStatus.FAILURE:
00258                 return TaskStatus.SUCCESS
00259             
00260             elif c.status == TaskStatus.FAILURE:
00261                 return TaskStatus.SUCCESS
00262             
00263             else:
00264                 return c.status
00265     
00266 class AutoRemoveSequence(Task):
00267     """ 
00268         Remove each successful subtask from a sequence 
00269     """
00270     def __init__(self, name, *args, **kwargs):
00271         super(AutoRemoveSequence, self).__init__(name, *args, **kwargs)
00272  
00273     def run(self):
00274         for c in self.children:
00275             c.status = c.run()
00276             
00277             if c.status == TaskStatus.FAILURE:
00278                 return TaskStatus.FAILURE
00279             
00280             if c.statuss == TaskStatus.RUNNING:
00281                 return TaskStatus.RUNNING
00282         
00283             try:
00284                 self.children.remove(self.children[0])
00285             except:
00286                 return TaskStatus.FAILURE
00287                 
00288         return TaskStatus.SUCCESS
00289     
00290 class CallbackTask(Task):
00291     """ 
00292         Turn any callback function (cb) into a task
00293     """
00294     def __init__(self, name, cb=None, cb_args=[], cb_kwargs={}, **kwargs):
00295         super(CallbackTask, self).__init__(name, cb=None, cb_args=[], cb_kwargs={}, **kwargs)
00296         
00297         self.name = name
00298         self.cb = cb
00299         self.cb_args = cb_args
00300         self.cb_kwargs = cb_kwargs
00301   
00302     def run(self):
00303         status = self.cb(*self.cb_args, **self.cb_kwargs)
00304          
00305         if status == 0 or status == False:
00306             return TaskStatus.FAILURE
00307         
00308         elif status == 1 or status == True:
00309             return TaskStatus.SUCCESS
00310         
00311         else:
00312             return TaskStatus.RUNNING            
00313             
00314 
00315 class loop(Task):
00316     """
00317         Loop over one or more subtasks a given number of iterations
00318     """
00319     def __init__(self, task, iterations=-1):
00320         new_name = task.name + "_loop_" + str(iterations)
00321         super(loop, self).__init__(new_name)
00322 
00323         self.task = task
00324         self.iterations = iterations
00325         self.old_run = task.run
00326         self.old_reset = task.reset
00327         self.old_children = task.children
00328         self.loop_count = 0
00329         
00330         print("Loop iterations: " + str(self.iterations))
00331         
00332     def run(self):
00333         if self.iterations != -1 and self.loop_count >= self.iterations:
00334             return TaskStatus.SUCCESS
00335 
00336         print("Loop " + str(self.loop_count))
00337             
00338         while True:
00339             self.status = self.old_run()
00340             
00341             if self.status == TaskStatus.SUCCESS:
00342                 break
00343             else:
00344                 return self.status
00345                 
00346         self.old_reset()
00347         self.loop_count += 1
00348 
00349         self.task.run = self.run
00350 
00351         return self.task
00352     
00353 class ignore_failure(Task):
00354     """
00355         Always return either RUNNING or SUCCESS.
00356     """
00357     def __init__(self, task):
00358         new_name = task.name + "_ignore_failure"
00359         super(ignore_failure, self).__init__(new_name)
00360 
00361         self.task = task
00362         self.old_run = task.run
00363         
00364     def run(self):
00365         while True:    
00366             self.status = self.old_run()
00367             
00368             if self.status == TaskStatus.FAILURE:
00369                 return TaskStatus.SUCCESS
00370             else:
00371                 return self.status
00372         
00373         self.task.run = self.run
00374         
00375         return self.task
00376     
00377 class task_not(Task):
00378     """
00379         Turn SUCCESS into FAILURE and vice-versa
00380     """
00381     def __init__(self, task):
00382         new_name = task.name + "_not"
00383         super(task_not, self).__init__(new_name)
00384 
00385         self.task = task
00386         self.old_run = task.run
00387         
00388     def run(self):
00389         while True:    
00390             self.status = self.old_run()
00391             
00392             if self.status == TaskStatus.FAILURE:
00393                 return TaskStatus.SUCCESS
00394             
00395             elif self.status == TaskStatus.FAILURE:
00396                 return TaskStatus.SUCCESS
00397             
00398             else:
00399                 return self.status
00400         
00401         self.task.run = self.run
00402         
00403         return self.task
00404 
00405 def print_tree(tree, indent=0):
00406     """
00407         Print an ASCII representation of the tree
00408     """
00409     for c in tree.children:
00410         print "    " * indent, "-->", c.name
00411         
00412         if c.children != []:
00413             print_tree(c, indent+1)
00414             
00415 def print_phpsyntax_tree(tree):    
00416     """
00417         Print an output compatible with ironcreek.net/phpSyntaxTree
00418     """
00419     for c in tree.children:
00420         print "[" + string.replace(c.name, "_", "."),
00421         if c.children != []:
00422             print_phpsyntax_tree(c),
00423         print "]",
00424     
00425 
00426 
00427 
00428 
00429 
00430 
00431 
00432 
00433 
00434 
00435 
00436 
00437 
00438 
00439 
00440 
00441 
00442 
00443 
00444 
00445 
00446 
00447 
00448 
00449 
00450     
00451     
00452     
00453     
00454     
00455     
00456     
00457                 
00458 
00459