00001
00002
00003
00004 import time
00005 import thread
00006 import sys
00007
00008 import roslib
00009 roslib.load_manifest('cob_script_server')
00010 import rospy
00011 import actionlib
00012
00013 class CobTaskException(Exception):
00014 def __init__(self, value):
00015 self.value = value
00016 def __str__(self):
00017 return repr(self.value)
00018
00019
00020
00021 class CobTask:
00022 """ A CobTask is a list of CobActions or other CobTasks. Each list element
00023 consists of a unique identifier, the CobAction itself and a dictionary which
00024 relates each possible return value of the current CobAction to the identifier
00025 of the next CobAction to be processed. The run-routine also provides means to
00026 check, if the script is generally in PAUSE-status."""
00027
00028 def __init__(self,description):
00029 self.description = "execute a task with Care-O-bot"
00030 self.actionList = []
00031 self.currentItem = ""
00032 self.pause = False
00033 self.editReturnValues = True
00034
00035 def run(self):
00036 """ The elements of the actionList are executed one by one. Depending on the
00037 return value, the appropriate reaction is chosen and the next element is
00038 processed """
00039
00040 rospy.loginfo(self.getDescription())
00041
00042 while True:
00043
00044 currentElement = self.__getElement(self.currentItem)
00045 rospy.logdebug("Processing item '%s'.",self.currentItem)
00046 rospy.loginfo(currentElement[1].getDescription())
00047 if currentElement == None:
00048 errorstring = "CobAction with identifier '" + self.currentItem + "' was not found in this CobTask!"
00049 rospy.logerr(errorstring)
00050 raise CobTaskException(errorstring)
00051
00052
00053 self.__waitOnPause()
00054
00055
00056 returnValue = apply(currentElement[1].run, currentElement[2])
00057
00058
00059 if self.__waitOnPause() == 1:
00060 print "TODO: Implement repetition of the action, if pause was on"
00061
00062
00063 print "TODO: send parameter for editing return value to parameter server"
00064 if self.editReturnValues:
00065 print "The processed function returned value >" + str(returnValue) + "<. ",
00066 print "Please type the return value (integer) you need for testing! ",
00067 print "Your options are:"
00068 print currentElement[3]
00069 returnValue = int(sys.stdin.readline())
00070
00071
00072
00073 if not returnValue in currentElement[3]:
00074 rospy.logerr("CobAction %s returned value '%d'. No reaction defined for that value!",self.currentItem,returnValue)
00075 rospy.logerr("Task will be aborted!")
00076 nextItem = "_abort_"
00077 else:
00078 nextItem = currentElement[3][returnValue]
00079
00080
00081 if nextItem == "_next_":
00082 idx = self.__getIndex(self.currentItem)
00083
00084 if not len(self.actionList) > idx+1:
00085 rospy.logwarn("Cannot process '_next_' item, as CobAction '%s' is last in the list", self.currentItem)
00086 rospy.logwarn("If this is intended, please use '_end_' or '_abort_' keyword!")
00087 return 0
00088
00089 nextItem = self.actionList[idx+1][0]
00090
00091
00092 if nextItem == "_end_":
00093 rospy.loginfo("Task execution ended sucessfully")
00094 return 0
00095 elif nextItem == "_abort_":
00096 rospy.loginfo("Task was aborted due to a failure")
00097 return 1
00098
00099
00100 rospy.logdebug("The next CobAction to be processed is '%s'.",nextItem)
00101 self.currentItem = nextItem
00102
00103
00104 def addAction(self,identifier, action, parameterTuple, background=False, reactionDict=None):
00105 """ generate one row in the actionList which is a list itself
00106 -> [uniqueIdentifier, CobAction, backgroundFlag, {returnValue: next uniqueIdentifier,...}"""
00107
00108
00109 if identifier in ("_next_","_end_","_abort_"):
00110 errorstring = "identifier in CobTask must not be '" + identifier + "' as this is a reserved word!"
00111 rospy.logerr(errorstring)
00112 raise CobTaskException(errorstring)
00113 for element in self.actionList:
00114 if element[0] == identifier:
00115 errorstring = "CobTask identifier '" + identifier + "' is already existing!"
00116 rospy.logerr(errorstring)
00117 raise CobTaskException(errorstring)
00118
00119
00120 if reactionDict == None:
00121 reactionDict2 = {0:"_next_"}
00122 else:
00123 reactionDict2 = reactionDict
00124 self.actionList.append([identifier, action, parameterTuple, reactionDict2])
00125
00126
00127 if len(self.actionList) == 1:
00128 self.currentItem = identifier
00129 rospy.logdebug("Element '%s' sucessfully added",identifier)
00130
00131 def setDescription(self,description):
00132 self.description = description
00133
00134 def getDescription(self):
00135 return self.description
00136
00137 def printActionList(self,indent=0):
00138 """ print action list in a human readable form including subtasks """
00139 if indent == 0:
00140 print "\nTask structure: ",self.getDescription()
00141 for element in self.actionList:
00142
00143 print " ",
00144 for i in range(indent):
00145 print "| ",
00146 print "|--",
00147
00148 print element[0] + " ----> " + str(element[3].values())
00149
00150 if isinstance(element[1],CobTask):
00151 element[1].printActionList(indent+1)
00152
00153 print "\n"
00154
00155
00156 def __getElement(self,identifier):
00157 for element in self.actionList:
00158 if element[0] == identifier:
00159 return element
00160 return None
00161
00162 def __hasElement(self,identifier):
00163 for element in self.actionList:
00164 if element[0] == identifier:
00165 return True
00166 return False
00167
00168 def __getIndex(self,identifier):
00169 for i in range(len(self.actionList)):
00170 if self.actionList[i][0] == identifier:
00171 return i
00172 return None
00173
00174 def __waitOnPause(self):
00175 print "TODO: Shift pause parameter to parameter server!"
00176 cycleTimeOnPause = 1
00177 pauseWasActive = False
00178
00179 while self.pause:
00180 if not pauseWasActive:
00181 rospy.loginfo("Skript entered Pause mode - Waiting for release")
00182 else:
00183 rospy.logdebug("Skript is still in Pause mode - Waiting for release")
00184 pauseWasActive = True
00185 time.sleep(cycleTimeOnPause)
00186
00187 if pauseWasActive:
00188 rospy.loginfo("Pause was released - Continuing script")
00189 return 1
00190 return 0
00191
00192
00193 def validateReactionReferences(self):
00194 """ Check if all references in the reaction dicts lead someware"""
00195 noOfWrongReferences = 0
00196 rospy.loginfo("The references in this CobTask are being checked...")
00197
00198
00199 for element in self.actionList:
00200
00201 for reference in element[3].values():
00202 if not self.__hasElement(reference) and reference not in ("_next_","_end_","_abort_"):
00203 rospy.logwarn("CobTask element '%s' refers to non-existing element '%s'!", element[0], reference)
00204 noOfWrongReferences = noOfWrongReferences + 1
00205
00206 if isinstance(element[1],CobTask):
00207 noOfWrongReferences = noOfWrongReferences + element[1].validateReactionReferences()
00208
00209 if noOfWrongReferences > 0:
00210 rospy.logwarn("%d references in CobTask are non-existing and will lead to exceptions when executed!",noOfWrongReferences)
00211 else:
00212 rospy.loginfo("References in CobTask seem to be valid!")
00213
00214 return noOfWrongReferences
00215
00216
00217
00218 class CobAction:
00219 """ this is the ordinary class for wrapping actions. Each main function is
00220 associated with one or more error handling functions which are executed, if the
00221 main functions return a value different from 0. The run-routine also provides
00222 means to check, if the script is generally in PAUSE-status."""
00223
00224 def __init__(self,mainFunctionTuple, errorFunctionTupleDict=None, revokeGoalOnPause=False):
00225 self.mainFunctionTuple = mainFunctionTuple
00226 self.errorFunctionTupleDict = errorFunctionTupleDict
00227 self.revokeGoalOnPause = revokeGoalOnPause
00228 self.description = "execute an action using ROS actionlib"
00229
00230 def run(self,parameterTuple):
00231 print "DUMMY: This routine has to be filled!"
00232 return 0
00233
00234 def setDescription(self,description):
00235 self.description = description
00236
00237 def getDescription(self):
00238 return self.description
00239
00240
00241
00242 class MoveJointAction(CobAction):
00243 """ This class extends CobAction and focusses on moving joints. Parameters can
00244 be trajectories or positions wrapped as trajectories"""
00245
00246 def __init__(self):
00247 self.description = "Move joint to specified position"
00248
00249 if __name__ == '__main__':
00250 action1 = CobAction(None)
00251 action2 = MoveJointAction()
00252 task1 = CobTask("Dies ist ein Testtask")
00253 task2 = CobTask("Der alles entscheidenede uebergeordnete Testtask")
00254
00255 task1.addElement("act1",action1,("Hello World",))
00256 task1.addElement("act2",action2,("Hello World",),{0:"_end_"})
00257
00258 task2.addElement("act3",action1,("Hello World",),{0:"_next_",2:"_abort_"})
00259 task2.addElement("tsk1",task1,(),{0:"_end_",1:"act3"})
00260
00261 task2.validateReactionReferences()
00262 task2.printActionList()
00263
00264