├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CMakeLists.txt ├── README.md ├── action └── DoSomething.action ├── launch └── dummy.launch ├── package.xml ├── requirements.txt ├── scripts ├── __init__.py ├── start_dummy_action_client.py └── start_dummy_action_server.py ├── setup.py └── src ├── __init__.py ├── actionlib_demo ├── __init__.py ├── dummy_action_client.py └── dummy_action_server.py └── actionlib_demo_tests ├── __init__.py └── __test_suite__.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # --- Created by https://www.gitignore.io/api/ros,python,pycharm 2 | 3 | ### PyCharm ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff: 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/dictionaries 11 | 12 | # Sensitive or high-churn files: 13 | .idea/**/dataSources/ 14 | .idea/**/dataSources.ids 15 | .idea/**/dataSources.xml 16 | .idea/**/dataSources.local.xml 17 | .idea/**/sqlDataSources.xml 18 | .idea/**/dynamic.xml 19 | .idea/**/uiDesigner.xml 20 | 21 | # Gradle: 22 | .idea/**/gradle.xml 23 | .idea/**/libraries 24 | 25 | # CMake 26 | cmake-build-debug/ 27 | 28 | # Mongo Explorer plugin: 29 | .idea/**/mongoSettings.xml 30 | 31 | ## File-based project format: 32 | *.iws 33 | 34 | ## Plugin-specific files: 35 | 36 | # IntelliJ 37 | /out/ 38 | 39 | # mpeltonen/sbt-idea plugin 40 | .idea_modules/ 41 | 42 | # JIRA plugin 43 | atlassian-ide-plugin.xml 44 | 45 | # Cursive Clojure plugin 46 | .idea/replstate.xml 47 | 48 | # Crashlytics plugin (for Android Studio and IntelliJ) 49 | com_crashlytics_export_strings.xml 50 | crashlytics.properties 51 | crashlytics-build.properties 52 | fabric.properties 53 | 54 | ### PyCharm Patch ### 55 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 56 | 57 | # *.iml 58 | # modules.xml 59 | # .idea/misc.xml 60 | # *.ipr 61 | 62 | # Sonarlint plugin 63 | .idea/sonarlint 64 | 65 | ### Python ### 66 | # Byte-compiled / optimized / DLL files 67 | __pycache__/ 68 | *.py[cod] 69 | *$py.class 70 | 71 | # C extensions 72 | *.so 73 | 74 | # Distribution / packaging 75 | .Python 76 | env/ 77 | #build/ # commented out for custom build directory 78 | develop-eggs/ 79 | dist/ 80 | downloads/ 81 | eggs/ 82 | .eggs/ 83 | lib/ 84 | lib64/ 85 | parts/ 86 | sdist/ 87 | var/ 88 | wheels/ 89 | *.egg-info/ 90 | .installed.cfg 91 | *.egg 92 | 93 | # PyInstaller 94 | # Usually these files are written by a python script from a template 95 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 96 | *.manifest 97 | *.spec 98 | 99 | # Installer logs 100 | pip-log.txt 101 | pip-delete-this-directory.txt 102 | 103 | # Unit test / coverage reports 104 | htmlcov/ 105 | .tox/ 106 | .coverage 107 | .coverage.* 108 | .cache 109 | nosetests.xml 110 | coverage.xml 111 | *,cover 112 | .hypothesis/ 113 | 114 | # Translations 115 | *.mo 116 | *.pot 117 | 118 | # Django stuff: 119 | *.log 120 | local_settings.py 121 | 122 | # Flask stuff: 123 | instance/ 124 | .webassets-cache 125 | 126 | # Scrapy stuff: 127 | .scrapy 128 | 129 | # Sphinx documentation 130 | docs/_build/ 131 | 132 | # PyBuilder 133 | target/ 134 | 135 | # Jupyter Notebook 136 | .ipynb_checkpoints 137 | 138 | # pyenv 139 | .python-version 140 | 141 | # celery beat schedule file 142 | celerybeat-schedule 143 | 144 | # SageMath parsed files 145 | *.sage.py 146 | 147 | # dotenv 148 | .env 149 | 150 | # virtualenv 151 | .venv 152 | venv/ 153 | ENV/ 154 | 155 | # Spyder project settings 156 | .spyderproject 157 | .spyproject 158 | 159 | # Rope project settings 160 | .ropeproject 161 | 162 | # mkdocs documentation 163 | /site 164 | 165 | ### ROS ### 166 | bin/ 167 | msg_gen/ 168 | srv_gen/ 169 | msg/*Action.msg 170 | msg/*ActionFeedback.msg 171 | msg/*ActionGoal.msg 172 | msg/*ActionResult.msg 173 | msg/*Feedback.msg 174 | msg/*Goal.msg 175 | msg/*Result.msg 176 | msg/_*.py 177 | 178 | # Generated by dynamic reconfigure 179 | *.cfgc 180 | /cfg/cpp/ 181 | /cfg/*.py 182 | 183 | # Ignore generated docs 184 | *.dox 185 | *.wikidoc 186 | 187 | # eclipse stuff 188 | .project 189 | .cproject 190 | 191 | # qcreator stuff 192 | CMakeLists.txt.user 193 | 194 | srv/_*.py 195 | *.pcd 196 | *.pyc 197 | qtcreator-* 198 | *.user 199 | 200 | /planning/cfg 201 | /planning/docs 202 | /planning/src 203 | 204 | *~ 205 | 206 | # Emacs 207 | .#* 208 | 209 | # Catkin custom files 210 | CATKIN_IGNORE 211 | 212 | # --- End of https://www.gitignore.io/api/ros,python,pycharm 213 | 214 | # MyPy Cache 215 | .mypy_cache 216 | 217 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(actionlib_demo) 3 | 4 | ## Compile as C++11, supported in ROS Kinetic and newer 5 | # add_compile_options(-std=c++11) 6 | 7 | ## Find catkin macros and libraries 8 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 9 | ## is used, also find other catkin packages 10 | find_package(catkin REQUIRED COMPONENTS 11 | rospy 12 | std_msgs 13 | message_generation 14 | genmsg 15 | actionlib_msgs 16 | actionlib 17 | ) 18 | 19 | ## System dependencies are found with CMake's conventions 20 | # find_package(Boost REQUIRED COMPONENTS system) 21 | 22 | 23 | ## Uncomment this if the package has a setup.py. This macro ensures 24 | ## modules and global scripts declared therein get installed 25 | ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html 26 | catkin_python_setup() 27 | 28 | ################################################ 29 | ## Declare ROS messages, services and actions ## 30 | ################################################ 31 | 32 | ## To declare and build messages, services or actions from within this 33 | ## package, follow these steps: 34 | ## * Let MSG_DEP_SET be the set of packages whose message types you use in 35 | ## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). 36 | ## * In the file package.xml: 37 | ## * add a build_depend tag for "message_generation" 38 | ## * add a build_depend and a run_depend tag for each package in MSG_DEP_SET 39 | ## * If MSG_DEP_SET isn't empty the following dependency has been pulled in 40 | ## but can be declared for certainty nonetheless: 41 | ## * add a run_depend tag for "message_runtime" 42 | ## * In this file (CMakeLists.txt): 43 | ## * add "message_generation" and every package in MSG_DEP_SET to 44 | ## find_package(catkin REQUIRED COMPONENTS ...) 45 | ## * add "message_runtime" and every package in MSG_DEP_SET to 46 | ## catkin_package(CATKIN_DEPENDS ...) 47 | ## * uncomment the add_*_files sections below as needed 48 | ## and list every .msg/.srv/.action file to be processed 49 | ## * uncomment the generate_messages entry below 50 | ## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) 51 | 52 | ## Generate messages in the 'msg' folder 53 | # add_message_files( 54 | # FILES 55 | # Message1.msg 56 | # Message2.msg 57 | # ) 58 | 59 | ## Generate services in the 'srv' folder 60 | # add_service_files( 61 | # FILES 62 | # Service1.srv 63 | # Service2.srv 64 | # ) 65 | 66 | # Generate actions in the 'action' folder 67 | add_action_files( 68 | FILES 69 | DoSomething.action 70 | ) 71 | 72 | ## Generate added messages and services with any dependencies listed here 73 | generate_messages( 74 | DEPENDENCIES 75 | std_msgs # Or other packages containing msgs 76 | actionlib_msgs 77 | ) 78 | 79 | ################################################ 80 | ## Declare ROS dynamic reconfigure parameters ## 81 | ################################################ 82 | 83 | ## To declare and build dynamic reconfigure parameters within this 84 | ## package, follow these steps: 85 | ## * In the file package.xml: 86 | ## * add a build_depend and a run_depend tag for "dynamic_reconfigure" 87 | ## * In this file (CMakeLists.txt): 88 | ## * add "dynamic_reconfigure" to 89 | ## find_package(catkin REQUIRED COMPONENTS ...) 90 | ## * uncomment the "generate_dynamic_reconfigure_options" section below 91 | ## and list every .cfg file to be processed 92 | 93 | ## Generate dynamic reconfigure parameters in the 'cfg' folder 94 | # generate_dynamic_reconfigure_options( 95 | # cfg/DynReconf1.cfg 96 | # cfg/DynReconf2.cfg 97 | # ) 98 | 99 | ################################### 100 | ## catkin specific configuration ## 101 | ################################### 102 | ## The catkin_package macro generates cmake config files for your package 103 | ## Declare things to be passed to dependent projects 104 | ## INCLUDE_DIRS: uncomment this if you package contains header files 105 | ## LIBRARIES: libraries you create in this project that dependent projects also need 106 | ## CATKIN_DEPENDS: catkin_packages dependent projects also need 107 | ## DEPENDS: system dependencies of this project that dependent projects also need 108 | catkin_package( 109 | # INCLUDE_DIRS include 110 | LIBRARIES actionlib_demo 111 | CATKIN_DEPENDS rospy std_msgs message_runtime actionlib_msgs 112 | # DEPENDS system_lib 113 | ) 114 | 115 | ########### 116 | ## Build ## 117 | ########### 118 | 119 | ## Specify additional locations of header files 120 | ## Your package locations should be listed before other locations 121 | include_directories( 122 | # include 123 | ${catkin_INCLUDE_DIRS} 124 | ) 125 | 126 | ## Declare a C++ library 127 | # add_library(${PROJECT_NAME} 128 | # src/${PROJECT_NAME} 129 | # ) 130 | 131 | ## Add cmake target dependencies of the library 132 | ## as an example, code may need to be generated before libraries 133 | ## either from message generation or dynamic reconfigure 134 | # add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) 135 | 136 | ## Declare a C++ executable 137 | ## With catkin_make all packages are built within a single CMake context 138 | ## The recommended prefix ensures that target names across packages don't collide 139 | # add_executable(${PROJECT_NAME}_node) 140 | 141 | ## Rename C++ executable without prefix 142 | ## The above recommended prefix causes long target names, the following renames the 143 | ## target back to the shorter version for ease of user use 144 | ## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" 145 | # set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") 146 | 147 | ## Add cmake target dependencies of the executable 148 | ## same as for the library above 149 | # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) 150 | 151 | ## Specify libraries to link a library or executable target against 152 | # target_link_libraries(${PROJECT_NAME}_node 153 | # ${catkin_LIBRARIES} 154 | # ) 155 | 156 | ############# 157 | ## Install ## 158 | ############# 159 | 160 | # all install targets should use catkin DESTINATION variables 161 | # See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html 162 | 163 | ## Mark executable scripts (Python etc.) for installation 164 | ## in contrast to setup.py, you can choose the destination 165 | # install(PROGRAMS 166 | # scripts/my_python_script 167 | # DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 168 | # ) 169 | 170 | ## Mark executables and/or libraries for installation 171 | # install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node 172 | # ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 173 | # LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 174 | # RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 175 | # ) 176 | 177 | ## Mark cpp header files for installation 178 | # install(DIRECTORY include/${PROJECT_NAME}/ 179 | # DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} 180 | # FILES_MATCHING PATTERN "*.h" 181 | # PATTERN ".svn" EXCLUDE 182 | # ) 183 | 184 | ## Mark other files for installation (e.g. launch and bag files, etc.) 185 | # install(FILES 186 | # # myfile1 187 | # # myfile2 188 | # DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 189 | # ) 190 | 191 | ############# 192 | ## Testing ## 193 | ############# 194 | 195 | ## Add gtest based cpp test target and link libraries 196 | # catkin_add_gtest(${PROJECT_NAME}-test) 197 | # if(TARGET ${PROJECT_NAME}-test) 198 | # target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) 199 | # endif() 200 | 201 | ## Add folders to be run by python nosetests 202 | # catkin_add_nosetests(test) 203 | #catkin_add_nosetests(src/test/example_listener_test.py) 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Actionlib Demo 2 | 3 | ## Description 4 | 5 | This is a simple demo for showing how to interact with action lib in Python 2.7. Actionlib is a powerfull asynchronous library built in ROS that will allow you to do multithreaded things without dealing with the threading overhead of Python 2.7. This demo will allow you to interact with two different actions independently of each other. You can start them with publishing to a topic, and cancel them by publishing to a different topic. See usage details below. 6 | 7 | ## Running the Nodes 8 | 9 | **Option 1:** Start all nodes with `roslaunch`. 10 | 11 | ``` 12 | $ roslaunch actionlib_demo dummy.launch --screen 13 | ``` 14 | 15 | _*The `--screen` flag logs output from all nodes to the terminal._ 16 | 17 | **Option 2:** Start nodes individually. 18 | 19 | ``` 20 | # Terminal 1 - The 'actionlib_demo_client' node. 21 | 22 | $ rosrun actionlib_demo start_dummy_action_client.py 23 | ``` 24 | 25 | ``` 26 | # Terminal 2 - The 'actionlib_demo_server' node. 27 | 28 | $ rosrun actionlib_demo start_dummy_action_server.py 29 | ``` 30 | ## Triggering an Action 31 | 32 | publish a message to topic "trigger_one" from a teminal to start action 1 33 | 34 | ``` 35 | $ rostopic pub /trigger_one std_msgs/String "data: ''" 36 | ``` 37 | 38 | publish a message to topic "trigger_two" from a terminal to start action 2 39 | 40 | ``` 41 | $ rostopic pub /trigger_two std_msgs/String "data: ''" 42 | ``` 43 | 44 | ## Cancelling an Action In Progress 45 | 46 | publish a mesage to topic "other_topic_one" from a terminal to cancel action 1 47 | 48 | ``` 49 | $ rostopic pub /other_topic_one std_msgs/String "data: ''" 50 | ``` 51 | 52 | publish a message to topic "other_topic_two" from a terminal to cancel action 2 53 | 54 | ``` 55 | $ rostopic pub /other_topic_two std_msgs/String "data: ''" 56 | ``` 57 | -------------------------------------------------------------------------------- /action/DoSomething.action: -------------------------------------------------------------------------------- 1 | # Define the goal 2 | uint32 how_long_to_do_something # specify how long to do the thing for 3 | --- 4 | # Define the result 5 | bool did_finish_doing_something # specify what the result is 6 | --- 7 | # Define a feedback message 8 | float32 percent_complete # specify a feedback metric -------------------------------------------------------------------------------- /launch/dummy.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | actionlib_demo 6 | 0.0.0 7 | This is a demo on how to interact with actionlib in python 2.7 8 | 9 | 10 | NickKnack15 11 | 12 | 13 | NickKnack15 14 | 15 | 16 | MIT 17 | 18 | 19 | catkin 20 | rospy 21 | rospy 22 | 23 | 24 | std_msgs 25 | std_msgs 26 | 27 | 28 | message_generation 29 | message_runtime 30 | 31 | actionlib 32 | actionlib 33 | 34 | actionlib_msgs 35 | actionlib_msgs 36 | 37 | 38 | rostest 39 | rosunit 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NickKnack15/python-actionlib-demo/ea7b618ee356e0c5c506f2219725b9bc6bf0d164/requirements.txt -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NickKnack15/python-actionlib-demo/ea7b618ee356e0c5c506f2219725b9bc6bf0d164/scripts/__init__.py -------------------------------------------------------------------------------- /scripts/start_dummy_action_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import rospy 3 | from actionlib_demo import DummyActionClient 4 | 5 | 6 | def start_actionlib_demo(): 7 | 8 | rospy.init_node('dummy_action_client', anonymous=False) # initialize ros node 9 | 10 | dummy_action_client = DummyActionClient() 11 | dummy_action_client.start() 12 | 13 | def stop_dummy_action_client(): 14 | dummy_action_client.stop() 15 | 16 | rospy.on_shutdown(stop_dummy_action_client) 17 | 18 | rospy.spin() # spin() simply keeps python from exiting until this node is stopped 19 | 20 | 21 | if __name__ == '__main__': 22 | start_actionlib_demo() 23 | -------------------------------------------------------------------------------- /scripts/start_dummy_action_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import rospy 3 | from actionlib_demo import DummyActionServer 4 | 5 | 6 | def start_actionlib_demo(): 7 | 8 | rospy.init_node('dummy_action_server', anonymous=False) # initialize ros node 9 | 10 | dummy_action_server = DummyActionServer() 11 | dummy_action_server.start() 12 | 13 | def stop_dummy_action_server(): 14 | dummy_action_server.stop() 15 | 16 | rospy.on_shutdown(stop_dummy_action_server) 17 | 18 | rospy.spin() # spin() simply keeps python from exiting until this node is stopped 19 | 20 | 21 | if __name__ == '__main__': 22 | start_actionlib_demo() 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD 2 | 3 | from distutils.core import setup 4 | from catkin_pkg.python_setup import generate_distutils_setup 5 | 6 | setup_args = generate_distutils_setup( 7 | packages=['actionlib_demo', 'actionlib_demo_tests'], 8 | package_dir={'': 'src'}) 9 | 10 | setup(**setup_args) 11 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NickKnack15/python-actionlib-demo/ea7b618ee356e0c5c506f2219725b9bc6bf0d164/src/__init__.py -------------------------------------------------------------------------------- /src/actionlib_demo/__init__.py: -------------------------------------------------------------------------------- 1 | from .dummy_action_server import DummyActionServer 2 | from .dummy_action_client import DummyActionClient 3 | -------------------------------------------------------------------------------- /src/actionlib_demo/dummy_action_client.py: -------------------------------------------------------------------------------- 1 | import rospy 2 | import actionlib 3 | import traceback 4 | from std_msgs.msg import String 5 | from actionlib_demo.msg import DoSomethingAction, DoSomethingActionFeedback, DoSomethingActionGoal, \ 6 | DoSomethingActionResult, DoSomethingFeedback, DoSomethingGoal, DoSomethingResult 7 | 8 | 9 | class DummyActionClient(object): 10 | def __init__(self): 11 | 12 | self.do_something_client = actionlib.SimpleActionClient('do_something', DoSomethingAction) 13 | 14 | self.do_something_else_client = actionlib.SimpleActionClient('do_something_else', DoSomethingAction) 15 | 16 | self.trigger_one_subscriber = rospy.Subscriber('trigger_one', String, self._trigger_one) 17 | self.trigger_two_subscriber = rospy.Subscriber('trigger_two', String, self._trigger_two) 18 | 19 | self.other_topic_one_subscriber = rospy.Subscriber('other_topic_one', String, self._other_topic_one) 20 | self.other_topic_two_subscriber = rospy.Subscriber('other_topic_two', String, self._other_topic_two) 21 | 22 | def start(self): 23 | self._loginfo('Action Client Started') 24 | self.do_something_client.wait_for_server(rospy.Duration.from_sec(15)) 25 | self.do_something_else_client.wait_for_server(rospy.Duration.from_sec(15)) 26 | 27 | def stop(self): 28 | self._loginfo('Action Client Stopped') 29 | self.do_something_client = None 30 | self.do_something_else_client = None 31 | 32 | def send_goal_one(self, length): 33 | try: 34 | goal = DoSomethingGoal() 35 | goal.how_long_to_do_something = length 36 | self.do_something_client.send_goal(goal, 37 | active_cb=self._goal_one_active, 38 | feedback_cb=self._goal_one_feedback, 39 | done_cb=self._goal_one_done) 40 | 41 | self._loginfo('goal one has been sent') 42 | 43 | except Exception as e: 44 | print(e.message) 45 | traceback.print_exc() 46 | self._loginfo('Error sending goal one') 47 | self.do_something_client.wait_for_server(rospy.Duration.from_sec(15)) 48 | self.send_goal_one(length) 49 | 50 | def _goal_one_active(self): 51 | self._loginfo('goal one has transitioned to active state') 52 | 53 | def _goal_one_feedback(self, feedback): 54 | # type: (DoSomethingFeedback) -> None 55 | self._loginfo('Goal one feedback received: {}'.format(feedback)) 56 | 57 | def _goal_one_done(self, state, result): 58 | # type: (actionlib.GoalStatus, DoSomethingResult) -> None 59 | self._loginfo('Goal one done callback triggered') 60 | self._loginfo(str(state)) 61 | self._loginfo(str(result)) 62 | self._loginfo('Do something result: ' + str(result.did_finish_doing_something)) 63 | 64 | def send_goal_two(self, length): 65 | try: 66 | goal = DoSomethingGoal() 67 | goal.how_long_to_do_something = length 68 | self.do_something_else_client.send_goal(goal, 69 | active_cb=self._goal_two_active, 70 | feedback_cb=self._goal_two_feedback, 71 | done_cb=self._goal_two_done) 72 | 73 | except Exception as e: 74 | print(e.message) 75 | traceback.print_exc() 76 | self._loginfo('Error sending goal two') 77 | self.do_something_else_client.wait_for_server(rospy.Duration.from_sec(15)) 78 | self.send_goal_one(length) 79 | 80 | def _goal_two_active(self): 81 | self._loginfo('goal two has transitioned to active state') 82 | 83 | def _goal_two_feedback(self, feedback): 84 | # type: (DoSomethingFeedback) -> None 85 | self._loginfo('Goal two feedback received: {}'.format(feedback)) 86 | 87 | def _goal_two_done(self, state, result): 88 | # type: (actionlib.GoalStatus, DoSomethingResult) -> None 89 | self._loginfo('Goal two done callback triggered') 90 | self._loginfo(str(state)) 91 | self._loginfo(str(result)) 92 | self._loginfo('Do something_else result: ' + str(result.did_finish_doing_something)) 93 | 94 | def _trigger_one(self, data): 95 | self._loginfo('Trigger one called') 96 | self._loginfo(data) 97 | 98 | self.send_goal_one(30) 99 | 100 | def _trigger_two(self, data): 101 | self._loginfo('Trigger two called') 102 | self._loginfo(data) 103 | 104 | self.send_goal_two(15) 105 | 106 | def _other_topic_one(self, data): 107 | self._loginfo('other_topic_one called') 108 | self._loginfo(data) 109 | self._loginfo('Preempting action do_something goal') 110 | self.do_something_client.cancel_goal() 111 | 112 | def _other_topic_two(self, data): 113 | self._loginfo('other_topic_two called') 114 | self._loginfo(data) 115 | self._loginfo('Preempting action do_something_else goal') 116 | self.do_something_else_client.cancel_goal() 117 | 118 | @staticmethod 119 | def _loginfo(message): 120 | # type: (str) -> None 121 | 122 | rospy.loginfo('DummyActionClient ({}) {}'.format('dummy_client', message)) 123 | -------------------------------------------------------------------------------- /src/actionlib_demo/dummy_action_server.py: -------------------------------------------------------------------------------- 1 | import time 2 | import rospy 3 | import actionlib 4 | from std_msgs.msg import String 5 | from actionlib_demo.msg import DoSomethingResult, DoSomethingGoal, DoSomethingFeedback, DoSomethingActionResult, \ 6 | DoSomethingActionGoal, DoSomethingActionFeedback, DoSomethingAction 7 | 8 | 9 | class DummyActionServer(object): 10 | def __init__(self): 11 | 12 | self.do_something_server = actionlib.SimpleActionServer('do_something', 13 | DoSomethingAction, 14 | self.do_something, 15 | auto_start=False) 16 | 17 | self.do_something_else_server = actionlib.SimpleActionServer('do_something_else', 18 | DoSomethingAction, 19 | self.do_something, 20 | auto_start=False) 21 | 22 | self.other_topic_subscriber = rospy.Subscriber('other_topic_one', String, self._other_topic_one) 23 | self.other_topic_two_subscriber = rospy.Subscriber('other_topic_two', String, self._other_topic_two) 24 | 25 | def __del__(self): 26 | self.do_something_server = None 27 | self.do_something_else_server = None 28 | 29 | def start(self): 30 | self._loginfo('Action Server Started') 31 | self.do_something_server.start() 32 | self.do_something_else_server.start() 33 | 34 | def stop(self): 35 | self._loginfo('Action Server Stopped') 36 | self.do_something_server = None 37 | self.do_something_else_server = None 38 | 39 | def do_something(self, goal): 40 | # type: (DoSomethingGoal) -> None 41 | self._loginfo('Action Server received do_something action request') 42 | success = True 43 | 44 | r = rospy.Rate(1) 45 | 46 | start_time = time.time() 47 | 48 | while not rospy.is_shutdown(): 49 | if time.time() - start_time > goal.how_long_to_do_something: 50 | break 51 | 52 | if self.do_something_server.is_preempt_requested(): 53 | self._loginfo('do_something action preempted') 54 | 55 | self.do_something_server.set_preempted() 56 | success = False 57 | break 58 | 59 | self._loginfo('Doing something action') 60 | r.sleep() 61 | 62 | if success: 63 | self._loginfo('do_something action succeeded') 64 | result = DoSomethingResult() 65 | result.did_finish_doing_something = True 66 | self.do_something_server.set_succeeded(result) 67 | 68 | def do_something_else(self, goal): 69 | # type: (DoSomethingGoal) -> None 70 | 71 | self._loginfo('Action server received do_something_else') 72 | success = True 73 | 74 | r = rospy.Rate(1) 75 | 76 | start_time = time.time() 77 | 78 | while not rospy.is_shutdown(): 79 | if time.time() - start_time > goal.how_long_to_do_something: 80 | break 81 | 82 | if self.do_something_else_server.is_preempt_requested(): 83 | self._loginfo('do_something_else action preempted') 84 | 85 | self.do_something_else_server.set_preempted() 86 | success = False 87 | break 88 | 89 | self._loginfo('Doing something_else action') 90 | r.sleep() 91 | 92 | if success: 93 | self._loginfo('do_something_else action succeeded') 94 | result = DoSomethingResult() 95 | result.did_finish_doing_something = True 96 | self.do_something_else_server.set_succeeded(result) 97 | 98 | def _other_topic_one(self, data): 99 | self._loginfo('other_topic_one subscriber callback triggered') 100 | self._loginfo(data) 101 | 102 | def _other_topic_two(self, data): 103 | self._loginfo('other_topic_two subscriber callback triggered') 104 | self._loginfo(data) 105 | 106 | @staticmethod 107 | def _loginfo(message): 108 | # type: (str) -> None 109 | 110 | rospy.loginfo('DummyActionServer ({}) {}'.format('dummy_server', message)) 111 | -------------------------------------------------------------------------------- /src/actionlib_demo_tests/__init__.py: -------------------------------------------------------------------------------- 1 | from .__test_suite__ import TestSuite 2 | -------------------------------------------------------------------------------- /src/actionlib_demo_tests/__test_suite__.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | 4 | 5 | class TestSuite(unittest.TestSuite): 6 | 7 | def __init__(self): 8 | super(TestSuite, self).__init__() 9 | 10 | test_directory = os.path.dirname(os.path.abspath(os.path.join(__file__, os.pardir))) 11 | loader = unittest.TestLoader() 12 | self.addTests(loader.discover(test_directory, '*_test.py')) 13 | --------------------------------------------------------------------------------