Codebase list ros-dynamic-reconfigure / 6af0e63
Imported Upstream version 1.5.39 Leopold Palomo-Avellaneda 9 years ago
51 changed file(s) with 4401 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 *.pyc
1 build/
0 .pyc
0 8ee968b00d67d5fef281c2a5e4f57115c8061d78 driver_common-0.2.0
1 06740ef3d1419a935ad295615950e32a2b2ec0a2 latest
2 52910ca5a9bcabbf6c6116150c574b7ea25968f5 latest
3 0ae185331a3f6b607d3bfa689cad9a720b54065a latest
4 d12220dd8225f637ae0d855982d8b854c11e959d driver_common-0.2.1
5 298bb34ff3b1c88bacfe450e5e7d9ce0428c9dcb latest
6 d374384b4aa4ca3c79a5a3acab1c86df902a5884 driver_common-0.2.2
7 0000000000000000000000000000000000000000 latest
8 bac516cacd4e74d200df0ee215c3068f8b4e9e4b latest
9 fb38d87fbb0a5876b1f3ccb0b29897e0f9da9dff driver_common-0.2.3
10 0000000000000000000000000000000000000000 latest
11 5c4e100ee2b0ec3b0ca6033ddddcb58971b54764 latest
12 8657c1160501274e3161233a568bfd4bd0b9bf48 driver_common-0.2.4
13 0000000000000000000000000000000000000000 latest
14 5504b07b16fd54e87166f3a108e9b8b9aab4fb24 latest
15 0000000000000000000000000000000000000000 driver_common-0.2.4
16 374e17886721dca4de1231ddfd7d4f2ef3505468 driver_common-0.2.4
17 0000000000000000000000000000000000000000 latest
18 397123de3765a33896a52ee5dc6648c4b597bd91 latest
19 bd5fb0d13554a71ea9ae340e035db07818c4e1dd driver_common-0.3.0
20 0000000000000000000000000000000000000000 latest
21 cbe4145931151e0dd9a8fc1aa841b555f28e3369 latest
22 7e57d7af4c17a9595962f0c11724af168e8414d1 driver_common-0.3.1
23 0000000000000000000000000000000000000000 latest
24 ec2ce404ddd01ef078ee1927b03ea53d9aa06b02 latest
25 c03eef1f042dcda192272b5057a9d94f3a4d4210 driver_common-1.0.0
26 0000000000000000000000000000000000000000 latest
27 8c54705f23e3315d25995f4b966081b9098d84be latest
28 5e1bcf51f56a22b040f02010d69322b73174426f driver_common-1.0.1
29 0000000000000000000000000000000000000000 latest
30 7279eeafbb6593f40d1ae54c7c3da0871048709f latest
31 b27c7ff9965b0d72b538ed738cdcd16cfd46d053 driver_common-1.0.2
32 0000000000000000000000000000000000000000 latest
33 9ae1425c1719849e9c93e8612310d98a1eebda9d latest
34 078e3f3d53b6e08fe218a4c6a892b607acd0c0bb driver_common-1.0.3
35 0000000000000000000000000000000000000000 driver_common-1.0.3
36 830d94a31cfd9116e2ed9ea707f0369f5e4843ca driver_common-1.0.3
37 0000000000000000000000000000000000000000 latest
38 8ad61da7b77f36684474c3e5f10f76c596ab586d latest
39 8ad61da7b77f36684474c3e5f10f76c596ab586d cturtle
40 4e43ef925cb37569582b8ba8ee39e0030dcbb8ad driver_common-1.0.4
41 0000000000000000000000000000000000000000 cturtle
42 5d8e3912773bb1ccabc2437ece1c2016a4d596e5 cturtle
43 ff840cde5207e40cc0a7ee389eaf44e367f635f7 driver_common-1.2.0
44 0000000000000000000000000000000000000000 cturtle
45 17da218fb89e1f350cc2fa2106daa706b3ea078b cturtle
46 17da218fb89e1f350cc2fa2106daa706b3ea078b unstable
47 0000000000000000000000000000000000000000 unstable
48 17da218fb89e1f350cc2fa2106daa706b3ea078b unstable
49 843eca5d14d6830d6107de8abdb83a771ffda446 driver_common-1.2.1
50 0000000000000000000000000000000000000000 cturtle
51 555a1ccbe400a403969264ff681ade6998ca3020 cturtle
52 0000000000000000000000000000000000000000 driver_common-1.2.1
53 ba52cf586cea09d5ce6c3705e34d393f6370c858 driver_common-1.2.1
54 0000000000000000000000000000000000000000 unstable
55 5470d28580c88a39b0ec9b95bdd61f049ec06f05 unstable
56 a31c85d9e66aa6e7421f833e6152902b0b5a399a driver_common-1.2.2
57 0000000000000000000000000000000000000000 cturtle
58 b42609f97b31de720460f5c72ea2b10c214b3042 cturtle
59 0000000000000000000000000000000000000000 driver_common-1.2.2
60 f1935191df16c6819e50822da7c9ac9d3f7d020d driver_common-1.2.2
61 0000000000000000000000000000000000000000 unstable
62 35fe604d0a26e7f6bd86ce553c6368519b986e33 unstable
63 5475d749e79d35f036949507bbf6bef028a372ac driver_common-1.2.3
64 0000000000000000000000000000000000000000 unstable
65 fa2447a319b8a95dfa69f0a6ceb5ffa18a85bb36 unstable
66 0000000000000000000000000000000000000000 driver_common-1.2.3
67 692a1f668d25855a3ade0855609db50f6dce777d driver_common-1.2.3
68 0000000000000000000000000000000000000000 cturtle
69 5ce3c0856ad36257d8f2e73f727227a1619298c0 cturtle
70 692a1f668d25855a3ade0855609db50f6dce777d diamondback
71 0caf2002fcb227c6374fefc9f1ef457476a25d83 dynamic_reconfigure-1.3.0
72 fa2447a319b8a95dfa69f0a6ceb5ffa18a85bb36 unstable
73 507b656e2d240c3564d947a62d6c816fdc36c3fc unstable
74 6a661d02b9ad66dad00a99593091715018271038 dynamic_reconfigure-1.4.0
75 3e3180e2b66a97713e1130717d00a68b53d42634 dynamic_reconfigure-1.5.0
76 226f459812467fc1e0588d347c6e360347819443 fuerte
77 e2fe213307ede1c84537fd4ac031ad7a0d9566ae dynamic_reconfigure-1.4.1
78 226f459812467fc1e0588d347c6e360347819443 fuerte
79 83ee7ade5de180a6fbc02ea7e7fa34ad3e44e6dd fuerte
80 b243ae76e67724f31703e9e9bb2bf4225ba0c532 dynamic_reconfigure-1.4.2
81 83ee7ade5de180a6fbc02ea7e7fa34ad3e44e6dd fuerte
82 43a2008df23ca4a9ad91e87053f52824ba62f7d2 fuerte
83 9f7838559e297c86e403d7e98e242fee1aca1825 1.5.0
84 f6220657e14e55d4e89102742487f7cd39dc3e47 1.5.1
85 ac6b0fc0dd25762a59d1c57bc711ff1d0d125459 1.5.2
86 79676e1211f6aa94bccc0a1e0ca20d764f9a32ea 1.5.3
87 968387f4630ef753bf276d4c2656fe164e80fcee 1.5.4
88 74cbeae9f657222b8c7e8fe43b8ba73c2ab17fae 1.5.5
89 437cb0932b06de3f471fe15aadb79e72fb00afa7 1.5.6
90 61194156e72275f53cc82055cbbc50cece6c2693 1.5.7
91 ef55784a184c8981ecf0bd122a217ab2b0f0c6eb 1.5.8
92 2b280d04b7dc031cb8afd3a19e41f11dbb26998e 1.5.9
93 0d1380d1f34d5ef417536cfc54f6efb7e1a2e67d 1,5,10
94 0d1380d1f34d5ef417536cfc54f6efb7e1a2e67d 1,5,10
95 0000000000000000000000000000000000000000 1,5,10
96 a7498ee3711b103e75414f9b4d67a18f92b63a14 1.5.10
97 0487e65370d0eda39189606436f2ff2fe93140fa 1.5.11
98 c9a6334afaa2f8c04d4a8939004926c345c91f83 1.5.12
99 28dd60e918e2f2d1cf9389b603d7183b92641572 1.5.13
100 2c0ff2fc8ed43c978b0ec3687129c039e206d308 1.5.14
101 9b1c9b70d24bd7e4448e53d353e68b88ea59448d 1.5.15
102 e7724acbca45949dd5584fa42dc502a40a4b9603 1.5.16
103 ab9222e987250b8a4aad6791b7f8b6bf8cf1538f 1.5.17
104 ffc5832d9ce71810ad8fed620ddf0a9d2172b403 1.5.18
105 9c23c8bfffae9534d14039dc6d60d9a427ab6d32 1.5.19
106 844ef175d560f4924e60445a71fec1a8b73a996d 1.5.20
107 7791d630874fa076f6404acdb01557cf0b0f0549 1.5.21
108 221f2aa2e604ecb189c5c9d92f6b85fcdf1cbcd6 1.5.22
109 72b26a1eb2ae59160e23230f7be1b219b895c418 1.5.23
110 c5f4a55f050c0b55f1a10f10893f0411347b1823 1.5.24
111 c7f8049b79f42e92b612d7041c5edb7978aa6780 1.5.25
112 5761c7a4c1c5ca932c7a74732eb7845aa03c47a4 1.5.26
113 a29ce24024f8df1eb5fc64be6c9d4425b90ff6d6 1.5.27
114 57da52e43938eded5cdbc047b603ebc9ab818726 1.5.28
115 aa2484286d6722639f5266a0cff0c840cd311965 1.5.29
116 3af6ae098c0146ac883a84a8658e3057d0e54e75 1.5.30
0 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1 Changelog for package dynamic_reconfigure
2 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3
4 1.5.39 (2015-04-22)
5 -------------------
6 * Better error message, to fix `#32 <https://github.com/ros/dynamic_reconfigure/issues/32>`_
7 * Make Python callback code consistent with the C++ API
8 * Commented unused parameters to avoid compile warnings
9 * Contributors: Esteve Fernandez, Morgan Quigley
10
11 1.5.38 (2014-12-23)
12 -------------------
13 * Fixes `#35 <https://github.com/ros/dynamic_reconfigure/issues/35>`_ by setting queue_size to 10 for publishers.
14 * Fixes `#31 <https://github.com/ros/dynamic_reconfigure/issues/31>`_ by removing boilerplate and copyright info from config header.
15 * Python 3 Support
16 * Honor BUILD_SHARED_LIBS and do not force building shared libraries.
17 * Unicode support
18 * Contributors: Basheer Subei, Esteve Fernandez, Gary Servin, Kei Okada, Scott K Logan
19
20 1.5.37 (2014-06-16)
21 -------------------
22 * Decode level of ParamDescription
23 * Added testsuite
24 * Avoid collisions with parameter names (`#6 <https://github.com/ros/dynamic_reconfigure/issues/6>`_)
25 * Contributors: Esteve Fernandez, pgorczak
0 cmake_minimum_required(VERSION 2.8)
1 project(dynamic_reconfigure)
2
3 find_package(catkin REQUIRED COMPONENTS message_generation roscpp std_msgs)
4 find_package(Boost REQUIRED COMPONENTS system thread)
5
6 include_directories(include ${catkin_INCLUDE_DIRS})
7 include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
8
9 catkin_python_setup()
10
11 add_message_files(
12 DIRECTORY msg
13 FILES
14 BoolParameter.msg Config.msg Group.msg IntParameter.msg SensorLevels.msg
15 ConfigDescription.msg DoubleParameter.msg GroupState.msg ParamDescription.msg StrParameter.msg)
16
17 add_service_files(
18 DIRECTORY srv
19 FILES Reconfigure.srv)
20
21 generate_messages(DEPENDENCIES std_msgs)
22
23 if(CATKIN_ENABLE_TESTING)
24 find_package(rostest REQUIRED)
25 set(dynamic_reconfigure_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
26 include(${dynamic_reconfigure_SOURCE_DIR}/cmake/dynamic_reconfigure-macros.cmake)
27 generate_dynamic_reconfigure_options(cfg/Test.cfg)
28 add_subdirectory(test)
29 endif()
30
31 catkin_package(LIBRARIES dynamic_reconfigure_config_init_mutex
32 INCLUDE_DIRS include
33 CATKIN_DEPENDS message_runtime std_msgs
34 CFG_EXTRAS dynamic_reconfigure-extras.cmake
35 )
36
37 add_library(dynamic_reconfigure_config_init_mutex
38 src/dynamic_reconfigure_config_init_mutex.cpp)
39 target_link_libraries(dynamic_reconfigure_config_init_mutex ${Boost_LIBRARIES})
40
41 install(DIRECTORY include/dynamic_reconfigure/
42 DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
43 FILES_MATCHING PATTERN "*.h")
44
45 install(TARGETS dynamic_reconfigure_config_init_mutex
46 ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
47 LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
48 RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION})
49
50 install(DIRECTORY cmake
51 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
52 USE_SOURCE_PERMISSIONS
53 )
54 install(DIRECTORY msg
55 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
56 install(PROGRAMS scripts/dynparam
57 DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
58 install(PROGRAMS scripts/reconfigure_gui
59 DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
60 install(DIRECTORY srv
61 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
62 install(DIRECTORY templates
63 DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2009, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33
34 PACKAGE='dynamic_reconfigure_test'
35
36 from dynamic_reconfigure.parameter_generator_catkin import *
37
38 gen = ParameterGenerator()
39 gen.const("int_const", int_t, 5, "An int constant.")
40 gen.const("double_const", double_t, 5.6, "A double constant.")
41 gen.const("str_const", str_t, "foo", "A string constant.")
42 gen.const("bool_const", bool_t, True, "A bool constant.")
43
44 enum = gen.enum([ gen.const("Small", int_t, 0, "A small constant"),
45 gen.const("Medium", int_t, 1, "A medium value"),
46 gen.const("Large", int_t, 2, "A large value"),
47 gen.const("ExtraLarge", int_t, 3, "An extra large value") ], "An enum to set the size.")
48
49 gen.add("int_enum_", int_t, 1, "Int enum",0, 0, 3, edit_method = enum)
50 gen.add("int_", int_t, 1, "Int parameter",0, -10, 10)
51 gen.add("double_", double_t, 2, "double parameter",0, -2, 10)
52 gen.add("str_", str_t, 4, "String parameter","foo")
53 gen.add("mstr_", str_t, 4, "Multibyte String parameter","bar")
54 gen.add("bool_", bool_t, 8, "Boolean parameter",False)
55 gen.add("level", int_t, 16, "Contains the level of the previous change",0)
56 gen.add("int_nodefault", int_t, 0, "Checks against regression of #4499")
57 gen.add("i", int_t, 0, "Checks against regression of https://github.com/ros/dynamic_reconfigure/issues/6")
58
59 group1 = gen.add_group("Group One", state = True)
60 group1.add("group1_int", int_t, 1, "A second level group parameter", 2)
61 group2 = group1.add_group("GROUP2", type="collapse", state=False)
62 group2.add("group2_double", double_t, 0, "A third level group parameter", 3.333)
63 group2.add("group2_string", str_t, 0, "A third level group parameter", "test string")
64 group2.add("some_other", str_t, 0, "Something", "AAAAAAGGHH")
65 group2.add("variable", bool_t,0, "A boolean", True)
66 group3 = group2.add_group("Group3")
67 group3.add("deep_var_int", int_t, 0, "Were very far down now", 0, 0, 3, edit_method = enum)
68 group3.add("deep_var_bool", bool_t, 0, "Were even farther down now!!", True)
69
70 group12 = gen.add_group("Upper Group 2")
71 group12.add("some_param", int_t, 0, "Some param", 20)
72
73 exit(gen.generate(PACKAGE, "test_reconfigure_server_cpp", "Test"))
0 # Return a list of all cfg/.cfg files
1 set(GENERATED_CFG_FILES "")
2
3 macro(add_generated_cfg)
4 list(APPEND GENERATED_CFG_FILES ${ARGV})
5 endmacro(add_generated_cfg)
6
7 macro(get_cfgs cfgvar)
8 file(GLOB _cfg_files RELATIVE "${PROJECT_SOURCE_DIR}/cfg" "${PROJECT_SOURCE_DIR}/cfg/*.cfg")
9 set(${cfgvar} ${GENERATED_CFG_FILES})
10 # Loop over each .cfg file, establishing a rule to compile it
11 foreach(_cfg ${_cfg_files})
12 # Make sure we didn't get a bogus match (e.g., .#Foo.cfg, which Emacs
13 # might create as a temporary file). the file()
14 # command doesn't take a regular expression, unfortunately.
15 if(${_cfg} MATCHES "^[^\\.].*\\.cfg$")
16 list(APPEND ${cfgvar} ${_cfg})
17 endif(${_cfg} MATCHES "^[^\\.].*\\.cfg$")
18 endforeach(_cfg)
19 endmacro(get_cfgs)
20
21 add_custom_target(rospack_gencfg ALL)
22 add_dependencies(rospack_genmsg_libexe rospack_gencfg)
23
24 macro(gencfg)
25 add_custom_target(rospack_gencfg_real ALL)
26 add_dependencies(rospack_gencfg_real rospack_gencfg)
27 include_directories(${PROJECT_SOURCE_DIR}/cfg/cpp)
28 endmacro(gencfg)
29
30 rosbuild_find_ros_package(dynamic_reconfigure)
31
32 macro(gencfg_cpp)
33 get_cfgs(_cfglist)
34 if (_cfglist)
35 set(_autogen "")
36 foreach(_cfg ${_cfglist})
37 message("MSG: gencfg_cpp on:" ${_cfg})
38 # Construct the path to the .cfg file
39 set(_input ${PROJECT_SOURCE_DIR}/cfg/${_cfg})
40
41 rosbuild_gendeps(${PROJECT_NAME} ${_cfg})
42
43 # The .cfg file is its own generator.
44 set(gencfg_cpp_exe "")
45
46 get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
47 set(gencfg_build_files
48 ${SELF_DIR}/../templates/ConfigType.py.template
49 ${SELF_DIR}/../templates/ConfigType.h.template
50 # ${dynamic_reconfigure_PACKAGE_PATH}/src/dynamic_reconfigure/parameter_generator.py
51 )
52
53 string(REPLACE ".cfg" "" _cfg_bare ${_cfg})
54
55 set(_output_cpp ${PROJECT_SOURCE_DIR}/cfg/cpp/${PROJECT_NAME}/${_cfg_bare}Config.h)
56 set(_output_dox ${PROJECT_SOURCE_DIR}/docs/${_cfg_bare}Config.dox)
57 set(_output_wikidoc ${PROJECT_SOURCE_DIR}/docs/${_cfg_bare}Config.wikidoc)
58 set(_output_usage ${PROJECT_SOURCE_DIR}/docs/${_cfg_bare}Config-usage.dox)
59 set(_output_py ${PROJECT_SOURCE_DIR}/src/${PROJECT_NAME}/cfg/${_cfg_bare}Config.py)
60
61 # Add the rule to build the .h the .cfg and the .msg
62 # FIXME Horrible hack. Can't get CMAKE to add dependencies for anything
63 # but the first output in add_custom_command.
64 execute_process(
65 COMMAND ${SELF_DIR}/gendeps ${_input}
66 ERROR_VARIABLE __gencfg_err
67 RESULT_VARIABLE __gencfg_result
68 OUTPUT_VARIABLE __gencfg_autodeps
69 OUTPUT_STRIP_TRAILING_WHITESPACE)
70 if (__gencfg_result)
71 message("ERROR [gendeps] ${__gencfg_result} ${__gencfg_err}")
72 else ()
73 if (__gencfg_err)
74 message("[gendeps] ${__gencfg_err}")
75 endif()
76 endif()
77 string(REPLACE "\n" " " ${_input}_AUTODEPS ${__gencfg_autodeps})
78 separate_arguments(${_input}_AUTODEPS)
79 #message("MSG: " ${${_input}_AUTODEPS})
80 add_custom_command(OUTPUT ${_output_cpp} ${_output_dox} ${_output_usage} ${_output_py} ${_output_wikidoc}
81 COMMAND ${gencfg_cpp_exe} ${_input}
82 DEPENDS ${_input} ${gencfg_cpp_exe} ${ROS_MANIFEST_LIST} ${gencfg_build_files} ${gencfg_extra_deps} ${${_input}_AUTODEPS})
83 list(APPEND _autogen ${_output_cpp} ${_output_msg} ${_output_getsrv} ${_output_setsrv}
84 ${_output_dox} ${_output_usage} ${_output_py})
85 endforeach(_cfg)
86 # Create a target that depends on the union of all the autogenerated
87 # files
88 add_custom_target(ROSBUILD_gencfg_cpp DEPENDS ${_autogen})
89 # Add our target to the top-level gencfg target, which will be fired if
90 # the user calls gencfg()
91 add_dependencies(rospack_gencfg ROSBUILD_gencfg_cpp)
92 else(_cfglist)
93 message(FATAL_ERROR "Dynamic reconfigure has been invoked but could not find a .cfg to build. Please add one to your package/cfg directory. Aborting.")
94 endif(_cfglist)
95 endmacro(gencfg_cpp)
96
97 # Call the macro we just defined.
98 gencfg_cpp()
0 #! /usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2009, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 str_t = "str"
34 bool_t = "bool"
35 int_t = "int"
36 double_t = "double"
37
38 class ParameterGenerator():
39 @staticmethod
40 def dummy(*params, **nparams):
41 return
42
43 def __getattr__(self, name):
44 return self.dummy
0 @[if DEVELSPACE]@
1 # base dir in develspace
2 set(dynamic_reconfigure_BASE_DIR "@(CMAKE_CURRENT_SOURCE_DIR)")
3 @[else]@
4 # base dir in installspace
5 set(dynamic_reconfigure_BASE_DIR "${dynamic_reconfigure_DIR}/..")
6 @[end if]@
7
8 include(${dynamic_reconfigure_BASE_DIR}/cmake/dynamic_reconfigure-macros.cmake)
0 macro(generate_dynamic_reconfigure_options)
1 if(${PROJECT_NAME}_CATKIN_PACKAGE)
2 message(FATAL_ERROR "generate_dynamic_reconfigure_options() must be called before catkin_package() in project '${PROJECT_NAME}'")
3 endif()
4
5 # ensure that package destination variables are defined
6 catkin_destinations()
7
8 set(_autogen "")
9 foreach(_cfg ${ARGN})
10 # Construct the path to the .cfg file
11 set(_input ${_cfg})
12 if(NOT IS_ABSOLUTE ${_input})
13 set(_input ${PROJECT_SOURCE_DIR}/${_input})
14 endif()
15
16 # The .cfg file is its own generator.
17 set(gencfg_build_files
18 ${dynamic_reconfigure_BASE_DIR}/templates/ConfigType.py.template
19 ${dynamic_reconfigure_BASE_DIR}/templates/ConfigType.h.template
20 )
21
22 get_filename_component(_cfgonly ${_cfg} NAME_WE)
23 set(_output_cpp ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_INCLUDE_DESTINATION}/${_cfgonly}Config.h)
24 set(_output_py ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/cfg/${_cfgonly}Config.py)
25 set(_output_dox ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_SHARE_DESTINATION}/docs/${_cfgonly}Config.dox)
26 set(_output_wikidoc ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_SHARE_DESTINATION}/docs/${_cfgonly}Config.wikidoc)
27 set(_output_usage ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_SHARE_DESTINATION}/docs/${_cfgonly}Config-usage.dox)
28
29 assert(CATKIN_ENV)
30 set(_cmd ${CATKIN_ENV}
31 ${_input}
32 ${dynamic_reconfigure_BASE_DIR}
33 ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_SHARE_DESTINATION}
34 ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_INCLUDE_DESTINATION}
35 ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}
36 )
37
38 #file(WRITE ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/cfg/__init__.py)
39
40 add_custom_command(OUTPUT
41 ${_output_cpp} ${_output_dox} ${_output_usage} ${_output_py} ${_output_wikidoc}
42 COMMAND ${_cmd}
43 DEPENDS ${_input} ${gencfg_build_files}
44 COMMENT "Generating dynamic reconfigure files from ${_cfg}: ${_output_cpp} ${_output_py}"
45 )
46
47 list(APPEND ${PROJECT_NAME}_generated
48 ${_output_cpp} ${_output_py}
49 )
50
51 install(FILES ${_output_cpp}
52 DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION})
53 endforeach(_cfg)
54
55 # gencfg target for hard dependency on dynamic_reconfigure generation
56 add_custom_target(${PROJECT_NAME}_gencfg ALL DEPENDS ${${PROJECT_NAME}_generated})
57
58 # register target for catkin_package(EXPORTED_TARGETS)
59 list(APPEND ${PROJECT_NAME}_EXPORTED_TARGETS ${PROJECT_NAME}_gencfg)
60
61 dynreconf_called()
62 endmacro()
63
64 macro(dynreconf_called)
65 if(NOT dynamic_reconfigure_CALLED)
66 set(dynamic_reconfigure_CALLED TRUE)
67
68 # mark that generate_dynamic_reconfigure_options() was called in order to detect wrong order of calling with catkin_python_setup()
69 set(${PROJECT_NAME}_GENERATE_DYNAMIC_RECONFIGURE TRUE)
70 # check if catkin_python_setup() installs an __init__.py file for a package with the current project name
71 # in order to skip the installation of a generated __init__.py file
72 set(package_has_static_sources ${${PROJECT_NAME}_CATKIN_PYTHON_SETUP_HAS_PACKAGE_INIT})
73
74 # generate empty __init__ to make parent folder of msg/srv a python module
75 if(NOT EXISTS ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/__init__.py)
76 file(WRITE ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/__init__.py "")
77 endif()
78 if(NOT package_has_static_sources)
79 # install package __init__.py
80 install(
81 FILES ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/__init__.py
82 DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
83 )
84 endif()
85
86 # make sure we can find generated messages and that they overlay all other includes
87 include_directories(BEFORE ${CATKIN_DEVEL_PREFIX}/${CATKIN_GLOBAL_INCLUDE_DESTINATION})
88 # pass the include directory to catkin_package()
89 list(APPEND ${PROJECT_NAME}_INCLUDE_DIRS ${CATKIN_DEVEL_PREFIX}/${CATKIN_GLOBAL_INCLUDE_DESTINATION})
90 # ensure that the folder exists
91 file(MAKE_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_GLOBAL_INCLUDE_DESTINATION})
92
93 # generate cfg module __init__.py
94 if(NOT EXISTS ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/cfg/__init__.py)
95 file(WRITE ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/cfg/__init__.py "")
96 endif()
97
98 # compile python code before installing
99 find_package(PythonInterp REQUIRED)
100 install(CODE "execute_process(COMMAND \"${PYTHON_EXECUTABLE}\" -m compileall \"${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/cfg\")")
101 install(
102 DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/cfg
103 DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
104 )
105 endif()
106 endmacro()
0 #! /usr/bin/env python
1 #
2 # Software License Agreement (BSD License)
3 #
4 # Copyright (c) 2010, Willow Garage, Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials provided
16 # with the distribution.
17 # * Neither the name of Willow Garage, Inc. nor the names of its
18 # contributors may be used to endorse or promote products derived
19 # from this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 #
34 # Revision $Id$
35
36 from __future__ import print_function
37
38 import sys
39 import inspect
40 import imp
41 import os.path
42
43 _DYNAMIC_RECONFIGURE_GENERATING_DEPENDENCIES = True
44
45 def do_nothing(*args):
46 pass
47
48 if __name__ == "__main__":
49 srcfile = sys.argv[1]
50
51 start_modules = list(sys.modules)
52 sys.exit = do_nothing
53 exit = do_nothing
54 #dynamic_reconfigure.parameter_generator.ParameterGenerator.generate = do_nothing
55
56 # Remove our directory from the system path, and replace it with the one
57 # that the file expects to see.
58 sys.path[0] = os.path.dirname(srcfile)
59 f = open(srcfile, 'U')
60 stderr = sys.stderr
61 stdout = sys.stdout
62 sys.stdout = stderr # Avoid clobbering our output.
63 print("Finding dependencies for %s" % srcfile, file=stderr)
64 try:
65 imp.load_module("__main__", f, srcfile, ('.cfg', 'U', 1))
66 except:
67 errmsg = "load_module did not return. Unable to determine dependencies for file listed above."
68 print('\n'.join(["*" * len(errmsg), errmsg, "*" * len(errmsg)]), file=stderr)
69 raise
70
71 end_modules = sys.modules
72 for m in end_modules:
73 if not m in start_modules:
74 try:
75 mod_file = inspect.getsourcefile(end_modules[m])
76 if type(mod_file) == str:
77 for i in range(50): # .eggs on mac are strange, move up the hierarchy until we find the .egg, bail after 50 tries.
78 if os.path.exists(mod_file):
79 print(mod_file, file=stdout)
80 break
81 mod_file = os.path.dirname(mod_file)
82 except:
83 pass
0 [epydoc]
1 name: dynamic_reconfigure
2 modules: dynamic_reconfigure
3 inheritance: included
4 url: http://ros.org/wiki/dynamic_reconfigure
5 frames: no
6 private: no
7 exclude: dynamic_reconfigure.parameter_generator, dynamic_reconfigure.msg, dynamic_reconfigure.srv, dynamic_reconfigure.cfg
0 #ifndef __DYNAMIC_RECONFIGURE__CONFIG_INIT_MUTEX_H__
1 #define __DYNAMIC_RECONFIGURE__CONFIG_INIT_MUTEX_H__
2
3 #include <boost/thread/mutex.hpp>
4
5 namespace dynamic_reconfigure
6 {
7 extern boost::mutex __init_mutex__;
8 }
9
10 #endif
0 #ifndef __DYNAMIC_RECONFIGURE__CONFIG_TOOLS__
1 #define __DYNAMIC_RECONFIGURE__CONFIG_TOOLS__
2
3 #include <string>
4 #include <vector>
5 #include <dynamic_reconfigure/Config.h>
6 #include <dynamic_reconfigure/Group.h>
7
8 namespace dynamic_reconfigure
9 {
10
11 class ConfigTools
12 {
13 public:
14 static std::vector<dynamic_reconfigure::BoolParameter> &getVectorForType(dynamic_reconfigure::Config &set, const bool /*val*/)
15 {
16 return set.bools;
17 }
18
19 static std::vector<dynamic_reconfigure::IntParameter> &getVectorForType(dynamic_reconfigure::Config &set, const int /*val*/)
20 {
21 return set.ints;
22 }
23
24 static std::vector<dynamic_reconfigure::StrParameter> &getVectorForType(dynamic_reconfigure::Config &set, const std::string& /*val*/)
25 {
26 return set.strs;
27 }
28
29 static std::vector<dynamic_reconfigure::DoubleParameter> &getVectorForType(dynamic_reconfigure::Config &set, const double /*val*/)
30 {
31 return set.doubles;
32 }
33
34 static const std::vector<dynamic_reconfigure::BoolParameter> &getVectorForType(const dynamic_reconfigure::Config &set, const bool /*val*/)
35 {
36 return set.bools;
37 }
38
39 static const std::vector<dynamic_reconfigure::IntParameter> &getVectorForType(const dynamic_reconfigure::Config &set, const int /*val*/)
40 {
41 return set.ints;
42 }
43
44 static const std::vector<dynamic_reconfigure::StrParameter> &getVectorForType(const dynamic_reconfigure::Config &set, const std::string& /*val*/)
45 {
46 return set.strs;
47 }
48
49 static const std::vector<dynamic_reconfigure::DoubleParameter> &getVectorForType(const dynamic_reconfigure::Config &set, const double /*val*/)
50 {
51 return set.doubles;
52 }
53
54 static dynamic_reconfigure::BoolParameter makeKeyValuePair(const std::string &name, const bool val)
55 {
56 dynamic_reconfigure::BoolParameter param;
57 param.name = name;
58 param.value = val ;
59 return param;
60 }
61
62 static dynamic_reconfigure::IntParameter makeKeyValuePair(const std::string &name, const int val)
63 {
64 dynamic_reconfigure::IntParameter param;
65 param.name = name;
66 param.value = val ;
67 return param;
68 }
69
70 static dynamic_reconfigure::StrParameter makeKeyValuePair(const std::string &name, const std::string &val)
71 {
72 dynamic_reconfigure::StrParameter param;
73 param.name = name;
74 param.value = val ;
75 return param;
76 }
77
78 static dynamic_reconfigure::DoubleParameter makeKeyValuePair(const std::string &name, const double val)
79 {
80 dynamic_reconfigure::DoubleParameter param;
81 param.name = name;
82 param.value = val ;
83 return param;
84 }
85
86 template <class T>
87 static void appendParameter(dynamic_reconfigure::Config &set, const std::string &name, const T &val)
88 {
89 getVectorForType(set, val).push_back(makeKeyValuePair(name, val));
90 }
91
92 template <class VT, class T>
93 static bool getParameter(const std::vector<VT> &vec, const std::string &name, T &val)
94 {
95 for (typename std::vector<VT>::const_iterator i = vec.begin(); i != vec.end(); ++i)
96 if (i->name == name)
97 {
98 val = i->value;
99 return true;
100 }
101 return false;
102 }
103
104 template <class T>
105 static bool getParameter(const dynamic_reconfigure::Config &set, const std::string &name, T &val)
106 {
107 return getParameter(getVectorForType(set, val), name, val);
108 }
109
110 template<class T>
111 static void appendGroup(dynamic_reconfigure::Config &set, const std::string &name, int id, int parent, const T &val)
112 {
113 dynamic_reconfigure::GroupState msg;
114 msg.name = name;
115 msg.id= id;
116 msg.parent = parent;
117 msg.state = val.state;
118 set.groups.push_back(msg);
119 }
120
121 template<class T>
122 static bool getGroupState(const dynamic_reconfigure::Config &msg, const std::string &name, T &val)
123 {
124 for(std::vector<dynamic_reconfigure::GroupState>::const_iterator i = msg.groups.begin(); i != msg.groups.end(); ++i)
125 if(i->name == name)
126 {
127 val.state = i->state;
128 return true;
129 }
130 return false;
131 }
132
133 static int size(dynamic_reconfigure::Config &msg)
134 {
135 return msg.bools.size() + msg.doubles.size() + msg.ints.size() + msg.strs.size();
136 }
137
138 static void clear(dynamic_reconfigure::Config &msg)
139 {
140 msg.bools.clear();
141 msg.ints.clear();
142 msg.strs.clear();
143 msg.doubles.clear();
144 msg.groups.clear();
145 }
146 };
147
148 }
149
150 #endif
0 /*********************************************************************
1 * Software License Agreement (BSD License)
2 *
3 * Copyright (c) 2009-2010, Willow Garage, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
16 * * Neither the name of the Willow Garage nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *********************************************************************/
33
34
35 /**
36
37 Author: Blaise Gassend
38
39 Handles synchronizing node state with the configuration server, and
40 handling of services to get and set configuration.
41
42 */
43
44 #ifndef __SERVER_H__
45 #define __SERVER_H__
46
47 #include <boost/function.hpp>
48 #include <boost/thread/recursive_mutex.hpp>
49 #include <ros/node_handle.h>
50 #include <dynamic_reconfigure/ConfigDescription.h>
51 #include <dynamic_reconfigure/Reconfigure.h>
52
53 /**
54 * @todo Add diagnostics.
55 */
56
57 namespace dynamic_reconfigure
58 {
59 /**
60 * Keeps track of the reconfigure callback function.
61 */
62 template <class ConfigType>
63 class Server
64 {
65 public:
66 Server(const ros::NodeHandle &nh = ros::NodeHandle("~")) :
67 node_handle_(nh),
68 mutex_(own_mutex_),
69 own_mutex_warn_(true)
70 {
71 init();
72 }
73
74 Server(boost::recursive_mutex &mutex, const ros::NodeHandle &nh = ros::NodeHandle("~")) :
75 node_handle_(nh),
76 mutex_(mutex),
77 own_mutex_warn_(false)
78 {
79 init();
80 }
81
82 typedef boost::function<void(ConfigType &, uint32_t level)> CallbackType;
83
84 void setCallback(const CallbackType &callback)
85 {
86 boost::recursive_mutex::scoped_lock lock(mutex_);
87 callback_ = callback;
88 callCallback(config_, ~0); // At startup we need to load the configuration with all level bits set. (Everything has changed.)
89 updateConfigInternal(config_);
90 }
91
92 void clearCallback()
93 {
94 boost::recursive_mutex::scoped_lock lock(mutex_);
95 callback_.clear();
96 }
97
98 void updateConfig(const ConfigType &config)
99 {
100 if (own_mutex_warn_)
101 {
102 ROS_WARN("updateConfig() called on a dynamic_reconfigure::Server that provides its own mutex. This can lead to deadlocks if updateConfig() is called during an update. Providing a mutex to the constructor is highly recommended in this case. Please forward this message to the node author.");
103 own_mutex_warn_ = false;
104 }
105 updateConfigInternal(config);
106 }
107
108
109 void getConfigMax(ConfigType &config)
110 {
111 config = max_;
112 }
113
114 void getConfigMin(ConfigType &config)
115 {
116 config = min_;
117 }
118
119 void getConfigDefault(ConfigType &config)
120 {
121 config = default_;
122 }
123
124 void setConfigMax(const ConfigType &config)
125 {
126 max_ = config;
127 PublishDescription();
128 }
129
130 void setConfigMin(const ConfigType &config)
131 {
132 min_ = config;
133 PublishDescription();
134 }
135
136 void setConfigDefault(const ConfigType &config)
137 {
138 default_ = config;
139 PublishDescription();
140 }
141
142
143 private:
144 ros::NodeHandle node_handle_;
145 ros::ServiceServer set_service_;
146 ros::Publisher update_pub_;
147 ros::Publisher descr_pub_;
148 CallbackType callback_;
149 ConfigType config_;
150 ConfigType min_;
151 ConfigType max_;
152 ConfigType default_;
153 boost::recursive_mutex &mutex_;
154 boost::recursive_mutex own_mutex_; // Used only if an external one isn't specified.
155 bool own_mutex_warn_;
156
157
158
159 void PublishDescription()
160 {
161 boost::recursive_mutex::scoped_lock lock(mutex_);
162 //Copy over min_ max_ default_
163 dynamic_reconfigure::ConfigDescription description_message = ConfigType::__getDescriptionMessage__();
164
165 max_.__toMessage__(description_message.max, ConfigType::__getParamDescriptions__(),ConfigType::__getGroupDescriptions__());
166 min_.__toMessage__(description_message.min,ConfigType::__getParamDescriptions__(),ConfigType::__getGroupDescriptions__());
167 default_.__toMessage__(description_message.dflt,ConfigType::__getParamDescriptions__(),ConfigType::__getGroupDescriptions__());
168
169 //Publish description
170 descr_pub_.publish(description_message);
171 }
172
173 void init()
174 {
175 //Grab copys of the data from the config files. These are declared in the generated config file.
176 min_ = ConfigType::__getMin__();
177 max_ = ConfigType::__getMax__();
178 default_ = ConfigType::__getDefault__();
179
180 boost::recursive_mutex::scoped_lock lock(mutex_);
181 set_service_ = node_handle_.advertiseService("set_parameters",
182 &Server<ConfigType>::setConfigCallback, this);
183
184 descr_pub_ = node_handle_.advertise<dynamic_reconfigure::ConfigDescription>("parameter_descriptions", 1, true);
185 descr_pub_.publish(ConfigType::__getDescriptionMessage__());
186
187 update_pub_ = node_handle_.advertise<dynamic_reconfigure::Config>("parameter_updates", 1, true);
188 ConfigType init_config = ConfigType::__getDefault__();
189 init_config.__fromServer__(node_handle_);
190 init_config.__clamp__();
191 updateConfigInternal(init_config);
192 }
193
194 void callCallback(ConfigType &config, int level)
195 {
196 if (callback_) // At startup we need to load the configuration with all level bits set. (Everything has changed.)
197 try {
198 callback_(config, level);
199 }
200 catch (std::exception &e)
201 {
202 ROS_WARN("Reconfigure callback failed with exception %s: ", e.what());
203 }
204 catch (...)
205 {
206 ROS_WARN("Reconfigure callback failed with unprintable exception.");
207 }
208 else
209 ROS_DEBUG("setCallback did not call callback because it was zero."); /// @todo kill this line.
210 }
211
212 bool setConfigCallback(dynamic_reconfigure::Reconfigure::Request &req,
213 dynamic_reconfigure::Reconfigure::Response &rsp)
214 {
215 boost::recursive_mutex::scoped_lock lock(mutex_);
216
217 ConfigType new_config = config_;
218 new_config.__fromMessage__(req.config);
219 new_config.__clamp__();
220 uint32_t level = config_.__level__(new_config);
221
222 callCallback(new_config, level);
223
224 updateConfigInternal(new_config);
225 new_config.__toMessage__(rsp.config);
226 return true;
227 }
228
229 void updateConfigInternal(const ConfigType &config)
230 {
231 boost::recursive_mutex::scoped_lock lock(mutex_);
232 config_ = config;
233 config_.__toServer__(node_handle_);
234 dynamic_reconfigure::Config msg;
235 config_.__toMessage__(msg);
236 update_pub_.publish(msg);
237 }
238 };
239
240 }
241 #endif
0 string name
1 bool value
0 BoolParameter[] bools
1 IntParameter[] ints
2 StrParameter[] strs
3 DoubleParameter[] doubles
4 GroupState[] groups
0 Group[] groups
1 Config max
2 Config min
3 Config dflt
0 string name
1 float64 value
0 string name
1 string type
2 ParamDescription[] parameters
3 int32 parent
4 int32 id
0 string name
1 bool state
2 int32 id
3 int32 parent
0 string name
1 int32 value
0 string name
1 string type
2 uint32 level
3 string description
4 string edit_method
0 # This message is deprecated, please use driver_base/SensorLevels instead.
1
2 byte RECONFIGURE_CLOSE = 3 # Parameters that need a sensor to be stopped completely when changed
3 byte RECONFIGURE_STOP = 1 # Parameters that need a sensor to stop streaming when changed
4 byte RECONFIGURE_RUNNING = 0 # Parameters that can be changed while a sensor is streaming
0 string name
1 string value
0 <package>
1 <name>dynamic_reconfigure</name>
2 <version>1.5.39</version>
3 <description>
4 This unary stack contains the dynamic_reconfigure package which provides a means to change
5 node parameters at any time without having to restart the node.
6 </description>
7 <maintainer email="esteve@osrfoundation.org">Esteve Fernandez</maintainer>
8 <license>BSD</license>
9
10 <url>http://ros.org/wiki/dynamic_reconfigure</url>
11 <author>Blaise Gassend</author>
12
13 <export>
14 <rosdoc config="rosdoc.yaml" />
15 </export>
16
17 <buildtool_depend version_gte="0.5.87">catkin</buildtool_depend>
18
19 <build_depend>boost</build_depend>
20 <build_depend>message_generation</build_depend>
21 <build_depend>roscpp</build_depend>
22 <build_depend>roscpp_serialization</build_depend>
23 <build_depend>rostest</build_depend>
24 <build_depend>std_msgs</build_depend>
25
26 <run_depend>boost</run_depend>
27 <run_depend>message_runtime</run_depend>
28 <run_depend>roscpp</run_depend>
29 <run_depend>roslib</run_depend>
30 <run_depend>rospy</run_depend>
31 <run_depend>rosservice</run_depend>
32 <run_depend>std_msgs</run_depend>
33 </package>
0 - builder: epydoc
1 config: epydoc.config
0 #! /usr/bin/env python
1 #
2 # Software License Agreement (BSD License)
3 #
4 # Copyright (c) 2009, Willow Garage, Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials provided
16 # with the distribution.
17 # * Neither the name of Willow Garage, Inc. nor the names of its
18 # contributors may be used to endorse or promote products derived
19 # from this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 #
34 # Revision $Id$
35
36 NAME='dynparam'
37 import roslib; roslib.load_manifest('dynamic_reconfigure')
38 import rospy
39 import optparse
40 import sys
41 import yaml
42 import dynamic_reconfigure.client
43
44 def do_list():
45 connect()
46 list = dynamic_reconfigure.find_reconfigure_services()
47 for s in list:
48 print s
49
50 def do_set_from_parameters():
51 usage = """Usage: %prog set_from_parameters [options] node
52
53 Example command line:
54 dynparam set_from_parameters wge100_camera _camera_url:=foo
55
56 Example launch file:
57 <launch>
58 <node name="$(anon adjust-wge100_camera)" pkg="dynamic_reconfigure" type="dynparam" args="set_from_parameters wge100_camera">
59 <param name="camera_url" value="foo" />
60 <param name="brightness" value="58" />
61 </node>
62 </launch>"""
63
64 parser = optparse.OptionParser(usage=usage, prog=NAME)
65 add_timeout_option(parser)
66 options, args = parser.parse_args(myargv[2:])
67 if len(args) == 0:
68 parser.error("invalid arguments. Please specify a node name")
69 elif len(args) > 1:
70 parser.error("too many arguments")
71 node = args[0]
72
73 connect()
74 try:
75 params = rospy.get_param("~")
76 except KeyError:
77 print >> sys.stderr, 'error updating parameters: no parameters found on parameter server'
78 return
79
80 set_params(node, params, timeout=options.timeout)
81
82 def do_set():
83 usage = """Usage: %prog set [options] node parameter value
84 or: %prog set [options] node values
85
86 Examples:
87 dynparam set wge100_camera camera_url foo
88 dynparam set wge100_camera "{'camera_url':'foo', 'brightness':58}" """
89
90 args, optparse_args = [], []
91 for s in myargv[2:]:
92 if s.startswith('-'):
93 if len(s) > 1 and ord(s[1]) >= ord('0') and ord(s[1]) <= ord('9'):
94 args.append(s)
95 else:
96 optparse_args.append(s)
97 else:
98 args.append(s)
99
100 parser = optparse.OptionParser(usage=usage, prog=NAME)
101 add_timeout_option(parser)
102 options, _ = parser.parse_args(optparse_args)
103 if len(args) > 3:
104 parser.error("too many arguments")
105 elif len(args) < 2:
106 parser.error("invalid arguments. Please specify either a node name, parameter name and parameter value, or a node name and a YAML dictionary")
107
108 node = args[0]
109 if len(args) == 2:
110 node, value = args[0], args[1]
111 values_dict = yaml.load(value)
112 if type(values_dict) != dict:
113 parser.error('invalid arguments. Please specify either a node name, parameter name and parameter value, or a node name and a YAML dictionary')
114 elif len(args) == 3:
115 node, parameter, value = args[0], args[1], args[2]
116 values_dict = { parameter : value }
117
118 connect()
119 try:
120 set_params(node, values_dict, timeout=options.timeout)
121 except rospy.service.ServiceException:
122 print 'couldn\'t set parameters at node %s' % node
123 except rospy.exceptions.ROSException:
124 print 'couldn\'t set parameters at node %s' % node
125
126 def do_get():
127 usage = "Usage: %prog get [options] node"
128
129 parser = optparse.OptionParser(usage=usage, prog=NAME)
130 add_timeout_option(parser)
131 options, args = parser.parse_args(myargv[2:])
132 if len(args) == 0:
133 parser.error("invalid arguments. Please specify a node name")
134 elif len(args) > 1:
135 parser.error("too many arguments")
136 node = args[0]
137
138 connect()
139 params = get_params(node, timeout=options.timeout)
140 if params is not None:
141 print params
142
143 def do_load():
144 usage = "Usage: %prog load [options] node file"
145
146 parser = optparse.OptionParser(usage=usage, prog=NAME)
147 add_timeout_option(parser)
148 options, args = parser.parse_args(myargv[2:])
149 if len(args) == 0:
150 parser.error("invalid arguments. Please specify a node name")
151 elif len(args) == 1:
152 parser.error("invalid arguments. Please specify an input file")
153 elif len(args) > 2:
154 parser.error("too many arguments")
155 node, path = args[0], args[1]
156
157 f = file(path, 'r')
158 try:
159 params = {}
160 for doc in yaml.load_all(f.read()):
161 params.update(doc)
162 finally:
163 f.close()
164
165 connect()
166 set_params(node, params, timeout=options.timeout)
167
168 def do_dump():
169 usage = "Usage: %prog dump [options] node file"
170
171 parser = optparse.OptionParser(usage=usage, prog=NAME)
172 add_timeout_option(parser)
173 options, args = parser.parse_args(myargv[2:])
174 if len(args) == 0:
175 parser.error("invalid arguments. Please specify a node name")
176 elif len(args) == 1:
177 parser.error("invalid arguments. Please specify an output file")
178 elif len(args) > 2:
179 parser.error("too many arguments")
180 node, path = args[0], args[1]
181
182 connect()
183 params = get_params(node, timeout=options.timeout)
184 if params is not None:
185 f = file(path, 'w')
186 try:
187 yaml.dump(params, f)
188 return
189 finally:
190 f.close()
191
192 print "couldn't get parameters from node %s" % node
193
194 def get_params(node, timeout=None):
195 client = dynamic_reconfigure.client.Client(node, timeout=timeout)
196 return client.get_configuration(timeout=timeout)
197
198 def set_params(node, params, timeout=None):
199 client = dynamic_reconfigure.client.Client(node, timeout=timeout)
200 try:
201 client.update_configuration(params)
202 except dynamic_reconfigure.DynamicReconfigureParameterException, e:
203 print 'error updating parameters: ' + str(e)
204
205 def add_timeout_option(parser):
206 parser.add_option('-t', '--timeout', action='store', type='float', default=None, help='timeout in secs')
207
208 def print_usage():
209 print """dynparam is a command-line tool for getting, setting, and
210 deleting parameters of a dynamically configurable node.
211
212 Commands:
213 \tdynparam set configure node
214 \tdynparam set_from_parameters copy configuration from parameter server
215 \tdynparam get get node configuration
216 \tdynparam load load configuration from file
217 \tdynparam dump dump configuration to file
218 \tdynparam list list configurable nodes
219
220 Type dynparam <command> -h for more detailed usage, e.g. 'dynparam get -h'
221 """
222 sys.exit(1)
223
224 def connect():
225 rospy.init_node('dynparam', anonymous=True)
226
227 if __name__ == '__main__':
228 myargv = rospy.myargv()
229 if len(myargv) == 1:
230 print_usage()
231 else:
232 cmd = myargv[1]
233 try:
234 if cmd == 'list': do_list()
235 elif cmd == 'set_from_parameters': do_set_from_parameters()
236 elif cmd == 'set': do_set()
237 elif cmd == 'get': do_get()
238 elif cmd == 'load': do_load()
239 elif cmd == 'dump': do_dump()
240 else: print_usage()
241 except rospy.exceptions.ROSInterruptException:
242 pass
0 #! /usr/bin/env python
1 #
2 # Software License Agreement (BSD License)
3 #
4 # Copyright (c) 2009, Willow Garage, Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials provided
16 # with the distribution.
17 # * Neither the name of Willow Garage, Inc. nor the names of its
18 # contributors may be used to endorse or promote products derived
19 # from this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 #
34
35 if __name__ == '__main__':
36 print '\033[91m' + "reconfigure_gui has moved!\n" + '\033[0m'
37 print " Try: " + '\033[92m' + "rosrun rqt_reconfigure rqt_reconfigure\n" + '\033[0m'
38 print " If you see this as part of a tutorial or a script, please update to reflect this change."
0 #!/usr/bin/env python
1
2 from distutils.core import setup
3 from catkin_pkg.python_setup import generate_distutils_setup
4
5 d = generate_distutils_setup(
6 packages=['dynamic_reconfigure'],
7 package_dir={'': 'src'},
8 requires=['roslib', 'rospy', 'rosservice']
9 )
10
11 setup(**d)
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2009, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials provided
14 # with the distribution.
15 # * Neither the name of Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31
32 """
33 Python client API for dynamic_reconfigure (L{Client}) as well as
34 example server implementation (L{Server}).
35 """
36
37 import roslib
38 import os
39
40 class DynamicReconfigureException(Exception):
41 """
42 dynamic_reconfigure base exception type
43 """
44 pass
45 class DynamicReconfigureParameterException(DynamicReconfigureException):
46 """
47 Exception for parameter errors.
48 """
49 pass
50 class DynamicReconfigureCallbackException(DynamicReconfigureException):
51 """
52 Exception for callback errors.
53 """
54 pass
55
56 def find_reconfigure_services():
57 import rosservice
58 return sorted([s[:-len('/set_parameters')] for s in rosservice.get_service_list() if s.endswith('/set_parameters')])
59
60 def get_parameter_names(descr):
61 return descr.defaults.keys()
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2009, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials provided
14 # with the distribution.
15 # * Neither the name of Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31
32 """
33 Python client API for dynamic_reconfigure (L{DynamicReconfigureClient}) as well as
34 example server implementation (L{DynamicReconfigureServer}).
35 """
36
37 from __future__ import with_statement
38
39 try:
40 import roslib; roslib.load_manifest('dynamic_reconfigure')
41 except:
42 pass
43 import rospy
44 import rosservice
45 import sys
46 import threading
47 import time
48 import types
49 from dynamic_reconfigure import DynamicReconfigureParameterException
50 from dynamic_reconfigure.srv import Reconfigure as ReconfigureSrv
51 from dynamic_reconfigure.msg import Config as ConfigMsg
52 from dynamic_reconfigure.msg import ConfigDescription as ConfigDescrMsg
53 from dynamic_reconfigure.msg import IntParameter, BoolParameter, StrParameter, DoubleParameter, ParamDescription
54 from dynamic_reconfigure.encoding import *
55
56 class Client(object):
57 """
58 Python dynamic_reconfigure client API
59 """
60 def __init__(self, name, timeout=None, config_callback=None, description_callback=None):
61 """
62 Connect to dynamic_reconfigure server and return a client object
63
64 @param name: name of the server to connect to (usually the node name)
65 @type name: str
66 @param timeout: time to wait before giving up
67 @type timeout: float
68 @param config_callback: callback for server parameter changes
69 @param description_callback: internal use only as the API has not stabilized
70 """
71 self.name = name
72 self.config = None
73 self.param_description = None
74 self.group_description = None
75
76 self._param_types = None
77
78 self._cv = threading.Condition()
79
80 self._config_callback = config_callback
81 self._description_callback = description_callback
82
83 self._set_service = self._get_service_proxy('set_parameters', timeout)
84 self._descriptions_sub = self._get_subscriber('parameter_descriptions', ConfigDescrMsg, self._descriptions_msg)
85 self._updates_sub = self._get_subscriber('parameter_updates', ConfigMsg, self._updates_msg)
86
87 def get_configuration(self, timeout=None):
88 """
89 Return the latest received server configuration (wait to receive
90 one if none have been received)
91
92 @param timeout: time to wait before giving up
93 @type timeout: float
94 @return: dictionary mapping parameter names to values or None if unable to retrieve config.
95 @rtype: {str: value}
96 """
97 if timeout is None or timeout == 0.0:
98 if self.get_configuration(timeout=1.0) is None:
99 print >> sys.stderr, 'Waiting for configuration...'
100
101 with self._cv:
102 while self.config is None:
103 if rospy.is_shutdown():
104 return None
105 self._cv.wait()
106 else:
107 start_time = time.time()
108 with self._cv:
109 while self.config is None:
110 if rospy.is_shutdown():
111 return None
112 secs_left = timeout - (time.time() - start_time)
113 if secs_left <= 0.0:
114 break
115 self._cv.wait(secs_left)
116
117 return self.config
118
119 def get_parameter_descriptions(self, timeout=None):
120 """
121 UNSTABLE. Return a description of the parameters for the server.
122 Do not use this method as the type that is returned may change.
123
124 @param timeout: time to wait before giving up
125 @type timeout: float
126 """
127 if timeout is None or timeout == 0.0:
128 with self._cv:
129 while self.param_description is None:
130 if rospy.is_shutdown():
131 return None
132 self._cv.wait()
133 else:
134 start_time = time.time()
135 with self._cv:
136 while self.param_description is None:
137 if rospy.is_shutdown():
138 return None
139 secs_left = timeout - (time.time() - start_time)
140 if secs_left <= 0.0:
141 break
142 self._cv.wait(secs_left)
143
144 return self.param_description
145
146 def get_group_descriptions(self, timeout=None):
147 if timeout is None or timeout == 0.0:
148 with self._cv:
149 while self.group_description is None:
150 if rospy.is_shutdown():
151 return None
152 self._cv.wait()
153 else:
154 start_time = time.time()
155 with self._cv:
156 while self.group_description is None:
157 if rospy.is_shutdown():
158 return None
159 secs_left = timeout - (time.time() - start_time)
160 if secs_left <= 0.0:
161 break
162 self._cv.wait(secs_left)
163
164 return self.group_description
165
166 def update_configuration(self, changes):
167 """
168 Change the server's configuration
169
170 @param changes: dictionary of key value pairs for the parameters that are changing
171 @type changes: {str: value}
172 """
173 # Retrieve the parameter descriptions
174 if self.param_description is None:
175 self.get_parameter_descriptions()
176
177 # Cast the parameters to the appropriate types
178 if self.param_description is not None:
179 for name, value in list(changes.items())[:]:
180 if not name is 'groups':
181 dest_type = self._param_types.get(name)
182 if dest_type is None:
183 raise DynamicReconfigureParameterException('don\'t know parameter: %s' % name)
184
185 try:
186 found = False
187 descr = [x for x in self.param_description if x['name'].lower() == name.lower()][0]
188
189 # Fix not converting bools properly
190 if dest_type is bool and type(value) is str:
191 changes[name] = value.lower() in ("yes", "true", "t", "1")
192 found = True
193 # Handle enums
194 elif type(value) is str and not descr['edit_method'] == '':
195 enum_descr = eval(descr['edit_method'])
196 found = False
197 for const in enum_descr['enum']:
198 if value.lower() == const['name'].lower():
199 val_type = self._param_type_from_string(const['type'])
200 changes[name] = val_type(const['value'])
201 found = True
202 if not found:
203 if sys.version_info.major < 3:
204 if type(value) is unicode:
205 changes[name] = unicode(value)
206 else:
207 changes[name] = dest_type(value)
208 else:
209 changes[name] = dest_type(value)
210
211 except ValueError as e:
212 raise DynamicReconfigureParameterException('can\'t set parameter \'%s\' of %s: %s' % (name, str(dest_type), e))
213
214 if 'groups' in changes.keys():
215 changes['groups'] = self.update_groups(changes['groups'])
216
217 config = encode_config(changes)
218 msg = self._set_service(config).config
219 if self.group_description is None:
220 self.get_group_descriptions()
221 resp = decode_config(msg, self.group_description)
222
223 return resp
224
225 def update_groups(self, changes):
226 """
227 Changes the servers group configuration
228
229 @param changes: dictionary of key value pairs for the parameters that are changing
230 @type changes: {str: value}
231 """
232
233 descr = self.get_group_descriptions()
234
235 groups = []
236 def update_state(group, description):
237 for p,g in description['groups'].items():
238 if g['name'] == group:
239 description['groups'][p]['state'] = changes[group]
240 else:
241 update_state(group, g)
242 return description
243
244 for change in changes:
245 descr = update_state(change, descr)
246
247 return descr
248
249 def close(self):
250 """
251 Close connections to the server
252 """
253 self._descriptions_sub.unregister()
254 self._updates_sub.unregister()
255
256 ## config_callback
257
258 def get_config_callback(self):
259 """
260 Retrieve the config_callback
261 """
262 return self._config_callback
263
264 def set_config_callback(self, value):
265 """
266 Set the config_callback
267 """
268 self._config_callback = value
269 if self._config_callback is not None:
270 self._config_callback(self.config)
271
272 config_callback = property(get_config_callback, set_config_callback)
273
274 ## description_callback
275
276 def get_description_callback(self):
277 """
278 Get the current description_callback
279 """
280 return self._config_callback
281
282 def set_description_callback(self, value):
283 """
284 UNSTABLE. Set the description callback. Do not use as the type of the
285 description callback may change.
286 """
287 self._description_callback = value
288 if self._description_callback is not None:
289 self._description_callback(self.param_description)
290
291 description_callback = property(get_description_callback, set_description_callback)
292
293 # Implementation
294
295 def _get_service_proxy(self, suffix, timeout):
296 service_name = rospy.resolve_name(self.name + '/' + suffix)
297 if timeout is None or timeout == 0.0:
298 try:
299 rospy.wait_for_service(service_name, 1.0)
300 except rospy.exceptions.ROSException:
301 print >> sys.stderr, 'Waiting for service %s...' % service_name
302 rospy.wait_for_service(service_name, timeout)
303 else:
304 rospy.wait_for_service(service_name, timeout)
305
306 return rospy.ServiceProxy(service_name, ReconfigureSrv)
307
308 def _get_subscriber(self, suffix, type, callback):
309 topic_name = rospy.resolve_name(self.name + '/' + suffix)
310
311 return rospy.Subscriber(topic_name, type, callback=callback)
312
313 def _updates_msg(self, msg):
314 if self.group_description is None:
315 self.get_group_descriptions()
316 self.config = decode_config(msg, self.group_description)
317
318 with self._cv:
319 self._cv.notifyAll()
320 if self._config_callback is not None:
321 self._config_callback(self.config)
322
323 def _descriptions_msg(self, msg):
324 self.group_description = decode_description(msg)
325 self.param_description = extract_params(self.group_description)
326
327 # Build map from parameter name to type
328 self._param_types = {}
329 for p in self.param_description:
330 n, t = p.get('name'), p.get('type')
331 if n is not None and t is not None:
332 self._param_types[n] = self._param_type_from_string(t)
333
334 with self._cv:
335 self._cv.notifyAll()
336 if self._description_callback is not None:
337 self._description_callback(self.param_description)
338
339 def _param_type_from_string(self, type_str):
340 if type_str == 'int': return int
341 elif type_str == 'double': return float
342 elif type_str == 'str': return str
343 elif type_str == 'bool': return bool
344 else:
345 raise DynamicReconfigureParameterException('parameter has unknown type: %s. This is a bug in dynamic_reconfigure.' % type_str)
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2009, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials provided
14 # with the distribution.
15 # * Neither the name of Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31
32 try:
33 import roslib; roslib.load_manifest('dynamic_reconfigure')
34 except:
35 pass
36 import rospy
37 import inspect
38 import copy
39 import sys
40
41 from dynamic_reconfigure.msg import Config as ConfigMsg
42 from dynamic_reconfigure.msg import ConfigDescription as ConfigDescrMsg
43 from dynamic_reconfigure.msg import Group as GroupMsg
44 from dynamic_reconfigure.msg import GroupState
45 from dynamic_reconfigure.msg import IntParameter, BoolParameter, StrParameter, DoubleParameter, ParamDescription
46
47 class Config(dict):
48 def __init__(self, *args, **kwargs):
49 dict.__init__(self, *args, **kwargs)
50
51 def __getstate__(self):
52 return self.__dict__.items()
53
54 def __setstate__(self, items):
55 for key, val in items:
56 self.__dict__[key] = val
57
58 def __repr__(self):
59 return super(Config, self).__repr__()
60
61 def __setitem__(self, key, value):
62 return super(Config, self).__setitem__(key, value)
63
64 def __getitem__(self, name):
65 return super(Config, self).__getitem__(name)
66
67 def __delitem__(self, name):
68 return super(Config, self).__delitem__(name)
69
70 __getattr__ = __getitem__
71 __setattr__ = __setitem__
72
73 def copy(self):
74 return Config(self)
75
76 def __deepcopy__(self, memo):
77 c = type(self)({})
78 for key, value in self.iteritems():
79 c[copy.deepcopy(key)] = copy.deepcopy(value)
80
81 return c
82
83
84 def encode_description(descr):
85 msg = ConfigDescrMsg()
86 msg.max = encode_config(descr.max)
87 msg.min = encode_config(descr.min)
88 msg.dflt = encode_config(descr.defaults)
89 msg.groups = encode_groups(None, descr.config_description)
90 return msg
91
92 def encode_groups(parent, group):
93 group_list = []
94
95 msg = GroupMsg()
96
97 msg.name = group['name']
98 msg.id = group['id']
99 msg.parent = group['parent']
100 msg.type = group['type']
101
102 for param in group['parameters']:
103 msg.parameters.append(ParamDescription(param['name'], param['type'], param['level'], param['description'], param['edit_method']))
104
105 group_list.append(msg)
106 for next in group['groups']:
107 group_list.extend(encode_groups(msg, next))
108
109 return group_list
110
111 def encode_config(config, flat=True):
112 msg = ConfigMsg()
113 for k, v in config.items():
114 ## @todo add more checks here?
115 if type(v) == int: msg.ints.append(IntParameter(k, v))
116 elif type(v) == bool: msg.bools.append(BoolParameter(k, v))
117 elif type(v) == str: msg.strs.append(StrParameter(k, v))
118 elif sys.version_info.major < 3 and type(v) == unicode:
119 msg.strs.append(StrParameter(k, v))
120 elif type(v) == float: msg.doubles.append(DoubleParameter(k, v))
121 elif type(v) == dict or isinstance(v, Config):
122 if flat is True:
123 def flatten(g):
124 groups = []
125 for name, group in g['groups'].items():
126 groups.extend(flatten(group))
127 groups.append(GroupState(group['name'], group['state'], group['id'], group['parent']))
128 return groups
129 msg.groups.append(GroupState(v['name'], v['state'], v['id'], v['parent']))
130 msg.groups.extend(flatten(v))
131 else:
132 msg.groups = [GroupState(x['name'], x['state'], x['id'], x['parent']) for x in v]
133
134 return msg
135
136 def group_dict(group):
137 try:
138 state = group.state
139 except AttributeError:
140 state = True
141 if hasattr(group, 'type'):
142 type = group.type
143 else:
144 type =''
145 return Config({
146 'id' : group.id,
147 'parent' : group.parent,
148 'name' : group.name,
149 'type' : type,
150 'state': state,
151 'groups' : Config({}),
152 'parameters' : Config({}),
153 })
154
155 def decode_description(msg):
156 mins = decode_config(msg.min)
157 maxes = decode_config(msg.max)
158 defaults = decode_config(msg.dflt)
159 groups = {}
160 grouplist = msg.groups
161
162 def params_from_msg(msg):
163 params = []
164 for param in msg.parameters:
165 name = param.name
166 params.append({
167 'name': name,
168 'min' : mins[name],
169 'max' : maxes[name],
170 'default' : defaults[name],
171 'type' : param.type,
172 'level': param.level,
173 'description' : param.description,
174 'edit_method' : param.edit_method,
175 })
176 return params
177
178 # grab the default group
179 for group in grouplist:
180 if group.id == 0:
181 groups = group_dict(group)
182 groups['parameters'] = params_from_msg(group)
183
184 def build_tree(group):
185 children = Config({})
186 for g in grouplist:
187 if g.id == 0:
188 pass
189 elif g.parent == group['id']:
190 gd = group_dict(g)
191
192 gd['parameters'] = params_from_msg(g)
193 gd['groups'] = build_tree(gd)
194 # add the dictionary into the tree
195 children[gd['name']] = gd
196 return children
197
198 groups['groups'] = build_tree(groups)
199
200 return groups
201
202 def get_tree(m, group = None):
203 if group is None:
204 for x in m.groups:
205 if x.id == 0:
206 group = x
207
208 children = Config({})
209 for g in m.groups:
210 if g.id == 0:
211 pass
212 elif g.parent == group.id:
213 gd = group_dict(g)
214
215 gd['groups'] = get_tree(m, g)
216 children[gd['name']] = gd
217
218 if group.id == 0:
219 ret = group_dict(group)
220 ret['groups'] = children
221 return ret
222 else:
223 return children
224
225 def initial_config(msg, description = None):
226 d = Config([(kv.name, kv.value) for kv in msg.bools + msg.ints + msg.strs + msg.doubles])
227 def gt(m, descr, group = None):
228 # get the default group
229 if group is None:
230 for x in m.groups:
231 if x.id == 0:
232 group = x
233
234 children = Config({})
235 for g in m.groups:
236 if g.id == 0:
237 pass
238 elif g.parent == group.id:
239 gd = group_dict(g)
240
241 def find_state(gr, dr):
242 for g in dr['groups']:
243 if g['id'] == gr['id']:
244 gr['state'] = g['state']
245 return
246 else:
247 find_state(gr, g)
248 return
249
250 find_state(gd, descr)
251
252 # Get the tree for this group
253 gd['groups'] = gt(m, descr, g)
254 children[gd['name']] = gd
255
256 if group.id == 0:
257 ret = group_dict(group)
258 ret['groups'] = children
259 return ret
260 else:
261 return children
262
263 if not msg.groups == [] and description is not None:
264 d["groups"] = gt(msg, description)
265
266 def add_params(group, descr):
267 for param in descr['parameters']:
268 group['parameters'][param['name']] = d[param['name']]
269 for n, g in group['groups'].items():
270 for dr in descr['groups']:
271 if dr['name'] == g['name']:
272 add_params(g, dr)
273
274 add_params(d['groups'], description)
275
276 return d
277
278 def decode_config(msg, description = None):
279 if sys.version_info.major < 3:
280 for s in msg.strs:
281 if not isinstance(s.value, unicode):
282 try:
283 s.value.decode('ascii')
284 except UnicodeDecodeError:
285 s.value = s.value.decode('utf-8')
286
287 d = Config([(kv.name, kv.value) for kv in msg.bools + msg.ints + msg.strs + msg.doubles])
288 if not msg.groups == [] and description is not None:
289 d["groups"] = get_tree(msg)
290
291 def add_params(group, descr):
292 for param in descr['parameters']:
293 if param['name'] in d.keys():
294 group[param['name']] = d[param['name']]
295 for n, g in group['groups'].items():
296 for nr, dr in descr['groups'].items():
297 if dr['name'] == g['name']:
298 add_params(g, dr)
299
300 add_params(d['groups'], description)
301
302 return d
303
304 def extract_params(group):
305 params = []
306 params.extend(group['parameters'])
307 try:
308 for n,g in group['groups'].items():
309 params.extend(extract_params(g))
310 except AttributeError:
311 for g in group['groups']:
312 params.extend(extract_params(g))
313 return params
314
315 def get_parents(group, descriptions):
316 parents = []
317 for p in descriptions['group']:
318 if p['id'] == group['parent']:
319 parents.extend(get_parents(p, descriptions))
320 parents.append(p)
321 return parents
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2009, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials provided
14 # with the distribution.
15 # * Neither the name of Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31
32 # Author: Blaise Gassend
33
34 # Given a set of parameters, generates the messages, service types, and
35 # classes to allow runtime reconfiguration. Documentation of a node's
36 # parameters is a handy byproduct.
37
38 ## @todo
39 # Need to check types of min max and default
40 # Need to put sane error on exceptions
41
42 import roslib; roslib.load_manifest("dynamic_reconfigure")
43 import roslib.packages
44 from string import Template
45 import os
46 import inspect
47 import string
48 import sys
49 import re
50
51 #LINEDEBUG="#line"
52 LINEDEBUG="//#line"
53
54 # Convenience names for types
55 str_t = "str"
56 bool_t = "bool"
57 int_t = "int"
58 double_t = "double"
59
60 id = 0
61
62 class ParameterGenerator:
63 minval = {
64 'int' : -0x80000000, #'INT_MIN',
65 'double' : -1e10000,#'-std::numeric_limits<double>::infinity()',
66 'str' : '',
67 'bool' : False,
68 }
69
70 maxval = {
71 'int' : 0x7FFFFFFF, #'INT_MAX',
72 'double' : 1e10000, #'std::numeric_limits<double>::infinity()',
73 'str' : '',
74 'bool' : True,
75 }
76
77 defval = {
78 'int' : 0,
79 'double' : 0,
80 'str' : '',
81 'bool' : False,
82 }
83
84 class Group:
85 instances = {}
86 def __init__(self, gen, name, type, state, id, parent):
87 self.name = name.replace(" ", "_")
88 self.type = type
89 self.groups = []
90 self.parameters =[]
91 self.gen = gen
92 self.id = id
93 self.parent = parent
94 self.state = state
95
96 self.srcline = inspect.currentframe().f_back.f_lineno
97 self.srcfile = inspect.getsourcefile(inspect.currentframe().f_back.f_code)
98
99 self.instances[self.id] = self
100
101 def get_group(self, id):
102 return self.instances[id]
103
104 def add_group(self, name, type="", state=True):
105 global id
106 group = self.gen.Group(self.gen, name, type, state, id, self.id)
107 id = id + 1
108 self.groups.append(group)
109 return group
110
111 def add(self, name, paramtype, level, description, default = None, min = None, max = None, edit_method = ""):
112 newparam = {
113 'name' : name,
114 'type' : paramtype,
115 'default' : default,
116 'level' : level,
117 'description' : description,
118 'min' : min,
119 'max' : max,
120 'srcline' : inspect.currentframe().f_back.f_lineno,
121 'srcfile' : inspect.getsourcefile(inspect.currentframe().f_back.f_code),
122 'edit_method' : edit_method,
123 }
124 if type == str_t and (max != None or min != None):
125 raise Exception("Max or min specified for %s, which is of string type"%name)
126 pattern = r'^[a-zA-Z][a-zA-Z0-9_]*$'
127 if not re.match(pattern, name):
128 raise Exception("The name of field \'%s\' does not follow the ROS naming conventions, see http://wiki.ros.org/ROS/Patterns/Conventions"%name)
129
130 self.gen.fill_type(newparam)
131 self.gen.check_type_fill_default(newparam, 'default', self.gen.defval[paramtype])
132 self.gen.check_type_fill_default(newparam, 'max', self.gen.maxval[paramtype])
133 self.gen.check_type_fill_default(newparam, 'min', self.gen.minval[paramtype])
134
135 self.parameters.append(newparam)
136
137 # Compile a list of all the parameters in this group
138 def get_parameters(self):
139 params = []
140 params.extend(self.parameters)
141 for group in self.groups:
142 params.extend(group.get_parameters())
143
144 return params
145
146 def get_parents(self):
147 parents = []
148 if not self.id == 0:
149 p = self.get_group(self.parent)
150 parents.extend(p.get_parents())
151 parents.append(self.name)
152 else:
153 parents.append(self.name)
154 return parents
155
156 def get_field(self):
157 fld = []
158 fld.extend(self.get_parents())
159 ret = []
160 for x in fld:
161 if x == self.name:
162 ret.append(string.lower(x))
163 else:
164 ret.append(string.upper(x))
165 return string.join(ret, "::")
166
167 def get_class(self, parent = False):
168 cls = []
169 cls.extend(self.get_parents())
170 cls = [string.upper(x) for x in cls]
171 if parent == True:
172 cls.pop()
173 return string.join(cls, "::")
174
175 # dictionary used to create the generated classes
176 def to_dict(self):
177 if self.id == 0:
178 name = "groups"
179 else:
180 name = self.name
181 if self.state:
182 state = 'true'
183 else:
184 state = 'false'
185 return {
186 'name': self.name,
187 'type': self.type,
188 'state': self.state,
189 'cstate': state,
190 'id':self.id, 'parent':self.parent,
191 'parameters': self.parameters,
192 'groups' : [group.to_dict() for group in self.groups],
193 'srcline' : self.srcline,
194 'srcfile' : self.srcfile,
195 'class' : self.get_class(),
196 'parentclass': self.get_class(parent=True),
197 'parentname': self.get_group(self.parent).name,
198 'field' : self.get_field(),
199 'upper': string.upper(self.name),
200 'lower': string.lower(name)
201 }
202
203
204 def pytype(self, drtype):
205 return { 'str':str, 'int':int, 'double':float, 'bool':bool }[drtype]
206
207
208 def check_type(self, param, field):
209 drtype = param['type']
210 name = param['name']
211 value = param[field]
212 pytype = self.pytype(drtype)
213 if pytype != type(value) and (pytype != float or type(value) != int):
214 raise TypeError("'%s' has type %s, but %s is %s"%(name, drtype, field, repr(value)))
215 param[field] = pytype(value)
216
217 def fill_type(self, param):
218 param['ctype'] = { 'str':'std::string', 'int':'int', 'double':'double', 'bool':'bool' }[param['type']]
219 param['cconsttype'] = { 'str':'const char * const', 'int':'const int', 'double':'const double', 'bool':'const bool' }[param['type']]
220
221 def check_type_fill_default(self, param, field, default):
222 value = param[field]
223 # If no value, use default.
224 if value == None:
225 param[field] = default
226 return
227 # Check that value type is compatible with type.
228 self.check_type(param, field)
229
230 def __init__(self):
231 global id
232 self.group = self.Group(self, "Default", "", True, 0, 0)
233 id = 1
234 self.constants = []
235 self.dynconfpath = roslib.packages.get_pkg_dir("dynamic_reconfigure")
236
237 def const(self, name, type, value, descr):
238 newconst = {
239 'name':name,
240 'type':type,
241 'value':value,
242 'srcline' : inspect.currentframe().f_back.f_lineno,
243 'srcfile' : inspect.getsourcefile(inspect.currentframe().f_back.f_code),
244 'description' : descr
245 }
246 self.fill_type(newconst)
247 self.check_type(newconst, 'value')
248 self.constants.append(newconst)
249 return newconst # So that we can assign the value easily.
250
251 def enum(self, constants, description):
252 if len(set(const['type'] for const in constants)) != 1:
253 raise Exception("Inconsistent types in enum!")
254 return repr({ 'enum' : constants, 'enum_description' : description })
255
256 # Wrap add and add_group for the default group
257 def add(self, name, paramtype, level, description, default = None, min = None, max = None, edit_method = ""):
258 self.group.add(name, paramtype, level, description, default, min, max, edit_method)
259
260 def add_group(self, name, type="", state=True):
261 return self.group.add_group(name, type=type, state=state)
262
263 def mkdirabs(self, path):
264 if os.path.isdir(path):
265 pass
266 elif os.path.isfile(path):
267 raise OSError("Error creating directory %s, a file with the same name exists" %path)
268 else:
269 head, tail = os.path.split(path)
270 if head and not os.path.isdir(head):
271 self.mkdir(head)
272 if tail:
273 try:
274 os.mkdir(path)
275 except OSError:
276 if not os.path.isdir(path):
277 raise
278
279 def mkdir(self, path):
280 path = os.path.join(self.pkgpath, path)
281 self.mkdirabs(path)
282
283 def generate(self, pkgname, nodename, name):
284 self.pkgname = pkgname
285 self.pkgpath = roslib.packages.get_pkg_dir(pkgname)
286 self.name = name
287 self.nodename = nodename
288 self.msgname = name+"Config"
289
290 # Don't regenerate headers if the config hasn't been modfied
291 cpp_header = os.path.realpath(os.path.join(self.pkgpath, "cpp", pkgname, self.msgname + ".h"))
292 if os.path.exists(cpp_header) and os.path.getmtime(os.path.realpath(__file__)) < os.path.getmtime(cpp_header):
293 exit(0)
294
295 try:
296 if sys.modules['__main__']._DYNAMIC_RECONFIGURE_GENERATING_DEPENDENCIES:
297 # Done this way because importing this module from gendeps
298 # causes imports of dynamic_reconfigure.msg to fail from at
299 # least some .cfg files. (Not sure why)
300 return
301 except:
302 pass
303 try:
304 #print '**************************************************************'
305 #print '**************************************************************'
306 print Template("Generating reconfiguration files for $name in $pkgname").\
307 substitute(name=self.name, pkgname = self.pkgname)
308 #print '**************************************************************'
309 #print '**************************************************************'
310 self.generatecpp()
311 self.generatedoc()
312 self.generatewikidoc()
313 self.generateusage()
314 self.generatepy()
315 self.deleteobsolete()
316 except Exception, e:
317 print "Error building srv %s.srv"%name
318 import traceback
319 traceback.print_exc()
320 exit(1)
321
322 def generatewikidoc(self):
323 self.mkdir("docs")
324 f = open(os.path.join(self.pkgpath, "docs", self.msgname+".wikidoc"), 'w')
325 print >> f, \
326 """# Autogenerated param section. Do not hand edit.
327 param {
328 group.0 {
329 name=Dynamically Reconfigurable Parameters
330 desc=See the [[dynamic_reconfigure]] package for details on dynamically reconfigurable parameters."""
331 i=-1
332 for param in self.group.get_parameters():
333 i=i+1
334 range = ""
335 try:
336 enum = eval(param['edit_method'])['enum']
337 range = ", ".join(Template("$name ($value): $description").substitute(const) for const in enum)
338 range = "Possible values are: " + range
339 except:
340 if param['type'] == int_t or param['type'] == double_t:
341 range = Template("Range: $min to $max").substitute(param)
342 print >> f, Template(
343 """$i.name= ~$name
344 $i.default= $default
345 $i.type= $type
346 $i.desc=$description $range"""
347 ).substitute(param, range = range, i = i)
348 print >> f,"}\n}\n# End of autogenerated section. You may edit below."
349 f.close()
350
351 def generateusage(self):
352 self.mkdir("docs")
353 f = open(os.path.join(self.pkgpath, "docs", self.msgname+"-usage.dox"), 'w')
354 #print >> f, "/**"
355 print >> f, "\\subsubsection usage Usage"
356 print >> f, '\\verbatim'
357 print >> f, Template('<node name="$nodename" pkg="$pkgname" type="$nodename">').\
358 substitute(pkgname = self.pkgname, nodename = self.nodename)
359 for param in self.group.get_parameters():
360 print >> f, Template(' <param name="$name" type="$type" value="$default" />').substitute(param)
361 print >> f, '</node>'
362 print >> f, '\\endverbatim'
363 print >> f
364 #print >> f, "*/"
365 f.close()
366
367 def generatedoc(self):
368 self.mkdir("docs")
369 f = open(os.path.join(self.pkgpath, "docs", self.msgname+".dox"), 'w')
370 #print >> f, "/**"
371 print >> f, "\\subsubsection parameters ROS parameters"
372 print >> f
373 print >> f, "Reads and maintains the following parameters on the ROS server"
374 print >> f
375 for param in self.group.get_parameters():
376 print >> f, Template("- \\b \"~$name\" : \\b [$type] $description min: $min, default: $default, max: $max").substitute(param)
377 print >> f
378 #print >> f, "*/"
379 f.close()
380
381 def generateusage(self):
382 self.mkdir("docs")
383 f = open(os.path.join(self.pkgpath, "docs", self.msgname+"-usage.dox"), 'w')
384 #print >> f, "/**"
385 print >> f, "\\subsubsection usage Usage"
386 print >> f, '\\verbatim'
387 print >> f, Template('<node name="$nodename" pkg="$pkgname" type="$nodename">').\
388 substitute(pkgname = self.pkgname, nodename = self.nodename)
389 for param in self.group.get_parameters():
390 print >> f, Template(' <param name="$name" type="$type" value="$default" />').substitute(param)
391 print >> f, '</node>'
392 print >> f, '\\endverbatim'
393 print >> f
394 #print >> f, "*/"
395 f.close()
396
397 def crepr(self, param, val):
398 type = param["type"]
399 if type == 'str':
400 return '"'+val+'"'
401 if type == 'int':
402 return str(val)
403 if type == 'double':
404 if val == float('inf'):
405 return 'std::numeric_limits<double>::infinity()'
406 elif val == -float('inf'):
407 return '-std::numeric_limits<double>::infinity()'
408 else:
409 return str(val)
410 if type == 'bool':
411 return { True : 1, False : 0 }[val]
412 raise TypeError(type)
413 # if type == 'string':
414 # return '"'+val+'"'
415 # if 'uint' in type:
416 # return str(val)+'ULL'
417 # if 'int' in type:
418 # return str(val)+'LL'
419 # if 'time' in type:
420 # return 'ros::Time('+str(val)+')'
421 # if 'duration' in type:
422 # return 'ros::Duration('+str(val)+')'
423 # if 'float' in types:
424 # return str(val)
425
426 def appendline(self, list, text, param, value = None):
427 if value == None:
428 val = ""
429 else:
430 val = self.crepr(param, param[value])
431 list.append(Template('${doline} $srcline "$srcfile"\n '+text).safe_substitute(param, v=val, doline=LINEDEBUG, configname=self.name))
432
433 def appendgroup(self, list, group):
434 subgroups = []
435 for g in group.groups:
436 self.appendgroup(subgroups, g)
437 setters = []
438 params = []
439 for p in group.parameters:
440 setters.append(Template(" if(\"${name}\"==(*_i)->name){${name} = boost::any_cast<${ctype}>(val);}").substitute(p));
441 params.append(Template("${ctype} ${name};").substitute(p));
442
443 subgroups = string.join(subgroups, "\n")
444 setters = string.join(setters, "\n")
445 params = string.join(params, "\n")
446 grouptemplate = open(os.path.join(self.dynconfpath, "templates", "GroupClass.h.template")).read()
447 list.append(Template(grouptemplate).safe_substitute(group.to_dict(), subgroups = subgroups, setters = setters, params = params, configname = self.name))
448
449 def generatecpp(self):
450 # Read the configuration manipulator template and insert line numbers and file name into template.
451 templatefile = os.path.join(self.dynconfpath, "templates", "ConfigType.h.template")
452 templatelines = []
453 templatefilesafe = templatefile.replace('\\', '\\\\') # line directive does backslash expansion.
454 curline = 1
455 f = open(templatefile)
456 for line in f:
457 curline = curline + 1
458 templatelines.append(Template(line).safe_substitute(linenum=curline,filename=templatefilesafe))
459 f.close()
460 template = ''.join(templatelines)
461
462 # Write the configuration manipulator.
463 cfg_cpp_dir = os.path.join("cfg", "cpp", self.pkgname)
464 self.mkdir(cfg_cpp_dir)
465 f = open(os.path.join(self.pkgpath, cfg_cpp_dir, self.name+"Config.h"), 'w')
466
467 paramdescr = []
468 groups = []
469 members = []
470 constants = []
471
472 for const in self.constants:
473 self.appendline(constants, "${cconsttype} ${configname}_${name} = $v;", const, "value")
474
475 def write_params(group):
476 if group.id == 0:
477 paramdescr.append(Template("${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config> ${name}(\"${name}\", \"${type}\", ${parent}, ${id}, ${cstate}, &${configname}Config::${lower});").safe_substitute(group.to_dict(), configname = self.name))
478 else:
479 paramdescr.append(Template("${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config::${parentclass}> ${name}(\"${name}\", \"${type}\", ${parent}, ${id}, ${cstate}, &${configname}Config::${field});").safe_substitute(group.to_dict(), configname = self.name))
480 for param in group.parameters:
481 self.appendline(members, "${ctype} ${name};", param)
482 self.appendline(paramdescr, "__min__.${name} = $v;", param, "min")
483 self.appendline(paramdescr, "__max__.${name} = $v;", param, "max")
484 self.appendline(paramdescr, "__default__.${name} = $v;", param, "default")
485 self.appendline(paramdescr, group.to_dict()['name']+".abstract_parameters.push_back(${configname}Config::AbstractParamDescriptionConstPtr(new ${configname}Config::ParamDescription<${ctype}>(\"${name}\", \"${type}\", ${level}, "\
486 "\"${description}\", \"${edit_method}\", &${configname}Config::${name})));", param)
487 self.appendline(paramdescr,
488 "__param_descriptions__.push_back(${configname}Config::AbstractParamDescriptionConstPtr(new ${configname}Config::ParamDescription<${ctype}>(\"${name}\", \"${type}\", ${level}, "\
489 "\"${description}\", \"${edit_method}\", &${configname}Config::${name})));", param)
490
491 for g in group.groups:
492 write_params(g)
493
494 self.appendline(paramdescr, "${name}.convertParams();", group.to_dict())
495 if group.id == 0:
496 self.appendline(paramdescr, "__group_descriptions__.push_back(${configname}Config::AbstractGroupDescriptionConstPtr(new ${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config>(${name})));", group.to_dict())
497 else:
498 self.appendline(paramdescr, "${parentname}.groups.push_back(${configname}Config::AbstractGroupDescriptionConstPtr(new ${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config::${parentclass}>(${name})));", group.to_dict())
499 self.appendline(paramdescr, "__group_descriptions__.push_back(${configname}Config::AbstractGroupDescriptionConstPtr(new ${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config::${parentclass}>(${name})));", group.to_dict())
500
501 write_params(self.group)
502 self.appendgroup(groups, self.group)
503
504 paramdescr = string.join(paramdescr, '\n')
505 members = string.join(members, '\n')
506 groups = string.join(groups, '\n')
507 constants = string.join(constants, '\n')
508 f.write(Template(template).substitute(uname=self.name.upper(),
509 configname=self.name, pkgname = self.pkgname, paramdescr = paramdescr,
510 members = members, groups = groups, doline = LINEDEBUG, constants = constants))
511 f.close()
512
513 def deleteoneobsolete(self, file):
514 try:
515 os.unlink(file)
516 except OSError:
517 pass
518
519 def deleteobsolete(self): ### @todo remove this after the transition period.
520 self.deleteoneobsolete(os.path.join(self.pkgpath, "msg", self.msgname+".msg"))
521 self.deleteoneobsolete(os.path.join("msg", "cpp", self.pkgpath, "msg", self.msgname+".msg"))
522 self.deleteoneobsolete(os.path.join(self.pkgpath, "srv", "Get"+self.msgname+".srv"))
523 self.deleteoneobsolete(os.path.join("srv", "cpp", self.pkgpath, "srv", "Get"+self.msgname+".srv"))
524 self.deleteoneobsolete(os.path.join(self.pkgpath, "srv", "Set"+self.msgname+".srv"))
525 self.deleteoneobsolete(os.path.join("srv", "cpp", self.pkgpath, "srv", "Set"+self.msgname+".srv"))
526
527 # def msgtype(self, type):
528 # return { 'int' : 'int32', 'bool' : 'int8', 'str' : 'string', 'double' : 'float64' }[type]
529 #
530 # def generatemsg(self):
531 # self.mkdir("msg")
532 # f = open(os.path.join(self.pkgpath, "msg", self.msgname+".msg"), 'w')
533 # print >> f, "# This is an autogerenated file. Please do not edit."
534 # print >> f, ""
535 # for param in self.parameters:
536 # print >> f, Template("$type $name # $description").substitute(param, type=self.msgtype(param['type']))
537 # f.close()
538 #
539 # def generategetsrv(self):
540 # self.mkdir("srv")
541 # f = open(os.path.join(self.pkgpath, "srv", "Get"+self.msgname+".srv"), 'w')
542 # print >> f, "# This is an autogerenated file. Please do not edit."
543 # print >> f, ""
544 # print >> f, "---"
545 # print >> f, self.msgname, "config", "# Current configuration of node."
546 # print >> f, self.msgname, "defaults", "# Minimum values where appropriate."
547 # print >> f, self.msgname, "min", "# Minimum values where appropriate."
548 # print >> f, self.msgname, "max", "# Maximum values where appropriate."
549 # f.close()
550 #
551 # def generatesetsrv(self):
552 # self.mkdir("srv")
553 # f = open(os.path.join(self.pkgpath, "srv", "Set"+self.msgname+".srv"), 'w')
554 # print >> f, "# This is an autogerenated file. Please do not edit."
555 # print >> f, self.msgname, "config", "# Requested node configuration."
556 # print >> f, "---"
557 # print >> f, self.msgname, "config", "# What the node's configuration was actually set to."
558 # f.close()
559
560 def generatepy(self):
561 # Read the configuration manipulator template and insert line numbers and file name into template.
562 templatefile = os.path.join(self.dynconfpath, "templates", "ConfigType.py.template")
563 templatelines = []
564 f = open(templatefile)
565 template = f.read()
566 f.close()
567
568 # Write the configuration manipulator.
569 self.mkdir(os.path.join("src", self.pkgname, "cfg"))
570 f = open(os.path.join(self.pkgpath, "src", self.pkgname, "cfg", self.name+"Config.py"), 'w')
571 f.write(Template(template).substitute(name = self.name,
572 pkgname = self.pkgname, pycfgdata = self.group.to_dict()))
573 for const in self.constants:
574 f.write(Template("${configname}_${name} = $v\n").
575 substitute(const, v = repr(const['value']),
576 configname=self.name))
577 f.close()
578
579 f = open(os.path.join(self.pkgpath, "src", self.pkgname, "cfg", "__init__.py"), 'a')
580 f.close()
581
582 f = open(os.path.join(self.pkgpath, "src", self.pkgname, "__init__.py"), 'a')
583 f.close()
584
585 f = open(os.path.join(self.pkgpath, "src", self.pkgname, "__init__.py"), 'a')
586 f.close()
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2009, Willow Garage, Inc.
3 # Copyright (c) 2015, Open Source Robotics Foundation, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 # Author: Blaise Gassend
34 # Maintainers: Esteve Fernandez, Morgan Quigley
35
36 # Given a set of parameters, generates the messages, service types, and
37 # classes to allow runtime reconfiguration. Documentation of a node's
38 # parameters is a handy byproduct.
39
40 ## @todo
41 # Need to check types of min max and default
42 # Need to put sane error on exceptions
43
44 from __future__ import print_function
45
46 from string import Template
47 import os
48 import inspect
49 import string
50 import sys
51 import re
52
53 #LINEDEBUG="#line"
54 LINEDEBUG="//#line"
55
56 # Convenience names for types
57 str_t = "str"
58 bool_t = "bool"
59 int_t = "int"
60 double_t = "double"
61
62 id = 0
63
64 class ParameterGenerator:
65 minval = {
66 'int' : -0x80000000, #'INT_MIN',
67 'double' : '-std::numeric_limits<double>::infinity()',
68 'str' : '',
69 'bool' : False,
70 }
71
72 maxval = {
73 'int' : 0x7FFFFFFF, #'INT_MAX',
74 'double' : 'std::numeric_limits<double>::infinity()',
75 'str' : '',
76 'bool' : True,
77 }
78
79 defval = {
80 'int' : 0,
81 'double' : 0,
82 'str' : '',
83 'bool' : False,
84 }
85
86 class Group:
87 instances = {}
88 def __init__(self, gen, name, type, state, id, parent):
89 self.name = name.replace(" ", "_")
90 self.type = type
91 self.groups = []
92 self.parameters =[]
93 self.gen = gen
94 self.id = id
95 self.parent = parent
96 self.state = state
97
98 self.srcline = inspect.currentframe().f_back.f_lineno
99 self.srcfile = inspect.getsourcefile(inspect.currentframe().f_back.f_code)
100
101 self.instances[self.id] = self
102
103 def get_group(self, id):
104 return self.instances[id]
105
106 def add_group(self, name, type="", state=True):
107 global id
108 group = self.gen.Group(self.gen, name, type, state, id, self.id)
109 id = id + 1
110 self.groups.append(group)
111 return group
112
113 def add(self, name, paramtype, level, description, default = None, min = None, max = None, edit_method = ""):
114 newparam = {
115 'name' : name,
116 'type' : paramtype,
117 'default' : default,
118 'level' : level,
119 'description' : description,
120 'min' : min,
121 'max' : max,
122 'srcline' : inspect.currentframe().f_back.f_lineno,
123 'srcfile' : inspect.getsourcefile(inspect.currentframe().f_back.f_code),
124 'edit_method' : edit_method,
125 }
126 if type == str_t and (max != None or min != None):
127 raise Exception("Max or min specified for %s, which is of string type"%name)
128 pattern = r'^[a-zA-Z][a-zA-Z0-9_]*$'
129 if not re.match(pattern, name):
130 raise Exception("The name of field \'%s\' does not follow the ROS naming conventions, see http://wiki.ros.org/ROS/Patterns/Conventions"%name)
131
132 self.gen.fill_type(newparam)
133 self.gen.check_type_fill_default(newparam, 'default', self.gen.defval[paramtype])
134 self.gen.check_type_fill_default(newparam, 'max', self.gen.maxval[paramtype])
135 self.gen.check_type_fill_default(newparam, 'min', self.gen.minval[paramtype])
136
137 self.parameters.append(newparam)
138
139 # Compile a list of all the parameters in this group
140 def get_parameters(self):
141 params = []
142 params.extend(self.parameters)
143 for group in self.groups:
144 params.extend(group.get_parameters())
145
146 return params
147
148 def get_parents(self):
149 parents = []
150 if not self.id == 0:
151 p = self.get_group(self.parent)
152 parents.extend(p.get_parents())
153 parents.append(self.name)
154 else:
155 parents.append(self.name)
156 return parents
157
158 def get_field(self):
159 fld = []
160 fld.extend(self.get_parents())
161 ret = []
162 for x in fld:
163 if x == self.name:
164 ret.append(x.lower())
165 else:
166 ret.append(x.upper())
167 return "::".join(ret)
168
169 def get_class(self, parent = False):
170 cls = []
171 cls.extend(self.get_parents())
172 cls = [x.upper() for x in cls]
173 if parent == True:
174 cls.pop()
175 return "::".join(cls)
176
177 # dictionary used to create the generated classes
178 def to_dict(self):
179 if self.id == 0:
180 name = "groups"
181 else:
182 name = self.name
183 if self.state:
184 state = 'true'
185 else:
186 state = 'false'
187 return {
188 'name': self.name,
189 'type': self.type,
190 'state': self.state,
191 'cstate': state,
192 'id':self.id, 'parent':self.parent,
193 'parameters': self.parameters,
194 'groups' : [group.to_dict() for group in self.groups],
195 'srcline' : self.srcline,
196 'srcfile' : self.srcfile,
197 'class' : self.get_class(),
198 'parentclass': self.get_class(parent=True),
199 'parentname': self.get_group(self.parent).name,
200 'field' : self.get_field(),
201 'upper': self.name.upper(),
202 'lower': name.lower()
203 }
204
205
206 def pytype(self, drtype):
207 return { 'str':str, 'int':int, 'double':float, 'bool':bool }[drtype]
208
209
210 def check_type(self, param, field):
211 drtype = param['type']
212 name = param['name']
213 value = param[field]
214 pytype = self.pytype(drtype)
215 if pytype != type(value) and (pytype != float or type(value) != int):
216 raise TypeError("'%s' has type %s, but %s is %s"%(name, drtype, field, repr(value)))
217 param[field] = pytype(value)
218
219 def fill_type(self, param):
220 param['ctype'] = { 'str':'std::string', 'int':'int', 'double':'double', 'bool':'bool' }[param['type']]
221 param['cconsttype'] = { 'str':'const char * const', 'int':'const int', 'double':'const double', 'bool':'const bool' }[param['type']]
222
223 def check_type_fill_default(self, param, field, default):
224 value = param[field]
225 # If no value, use default.
226 if value == None:
227 param[field] = default
228 return
229 # Check that value type is compatible with type.
230 self.check_type(param, field)
231
232 def __init__(self):
233 global id
234 self.group = self.Group(self, "Default", "", True, 0, 0)
235 id = 1
236 self.constants = []
237 if len(sys.argv) < 5:
238 msg = """
239 ahhhh! Unexpected command line syntax!
240
241 Are you trying to call a dynamic_reconfigure configuration generation script
242 directly? When you are using dynamic_reconfigure with python, you don't ever
243 need to invoke the configuration generator script yourself; it loads
244 automatically. If you are using dynamic_reconfigure from C++, you need to
245 add a call to generate_dynamic_reconfigure_options() in your CMakeLists.txt
246
247 For an example, see http://wiki.ros.org/dynamic_reconfigure/Tutorials
248
249 Have a nice day
250 """
251 print(msg)
252 sys.exit(1)
253 self.dynconfpath = sys.argv[1] # FIXME this is awful
254 self.binary_dir = sys.argv[2]
255 self.cpp_gen_dir = sys.argv[3]
256 self.py_gen_dir = sys.argv[4]
257
258 def const(self, name, type, value, descr):
259 newconst = {
260 'name':name,
261 'type':type,
262 'value':value,
263 'srcline' : inspect.currentframe().f_back.f_lineno,
264 'srcfile' : inspect.getsourcefile(inspect.currentframe().f_back.f_code),
265 'description' : descr
266 }
267 self.fill_type(newconst)
268 self.check_type(newconst, 'value')
269 self.constants.append(newconst)
270 return newconst # So that we can assign the value easily.
271
272 def enum(self, constants, description):
273 if len(set(const['type'] for const in constants)) != 1:
274 raise Exception("Inconsistent types in enum!")
275 return repr({ 'enum' : constants, 'enum_description' : description })
276
277 # Wrap add and add_group for the default group
278 def add(self, name, paramtype, level, description, default = None, min = None, max = None, edit_method = ""):
279 self.group.add(name, paramtype, level, description, default, min, max, edit_method)
280
281 def add_group(self, name, type="", state=True):
282 return self.group.add_group(name, type=type, state=state)
283
284 def mkdirabs(self, path):
285 if os.path.isdir(path):
286 pass
287 elif os.path.isfile(path):
288 raise OSError("Error creating directory %s, a file with the same name exists" %path)
289 else:
290 try:
291 os.makedirs(path)
292 except OSError:
293 if not os.path.isdir(path):
294 raise
295
296 def generate(self, pkgname, nodename, name):
297 """
298 name must match the first part of the configuration file.
299 e.g. given Tutorials.cfg, name must be Tutorials
300 """
301 try:
302 if sys.modules['__main__']._DYNAMIC_RECONFIGURE_GENERATING_DEPENDENCIES:
303 # Done this way because importing this module from gendeps
304 # causes imports of dynamic_reconfigure.msg to fail from at
305 # least some .cfg files. (Not sure why)
306 return
307 except:
308 pass
309 try:
310 self.pkgname = pkgname
311 self.name = name
312 self.nodename = nodename
313 self.msgname = name+"Config"
314 #print '**************************************************************'
315 #print '**************************************************************'
316 print(Template("Generating reconfiguration files for $name in $pkgname").\
317 substitute(name=self.name, pkgname = self.pkgname))
318 #print '**************************************************************'
319 #print '**************************************************************'
320 self.generatecpp()
321 self.generatedoc()
322 self.generatewikidoc()
323 self.generateusage()
324 self.generatepy()
325 #self.deleteobsolete()
326 except Exception:
327 print("Error building srv %s.srv" % name)
328 import traceback
329 traceback.print_exc()
330 exit(1)
331
332 def generatewikidoc(self):
333 self.mkdirabs(os.path.join(self.binary_dir, "docs"))
334 f = open(os.path.join(self.binary_dir, "docs", self.msgname+".wikidoc"), 'w')
335 print(
336 """# Autogenerated param section. Do not hand edit.
337 param {
338 group.0 {
339 name=Dynamically Reconfigurable Parameters
340 desc=See the [[dynamic_reconfigure]] package for details on dynamically reconfigurable parameters.""", file=f)
341 i=-1
342 for param in self.group.parameters:
343 i=i+1
344 range = ""
345 try:
346 enum = eval(param['edit_method'])['enum']
347 range = ", ".join(Template("$name ($value): $description").substitute(const) for const in enum)
348 range = "Possible values are: " + range
349 except:
350 if param['type'] == int_t or param['type'] == double_t:
351 range = Template("Range: $min to $max").substitute(param)
352 print(Template(
353 """$i.name= ~$name
354 $i.default= $default
355 $i.type= $type
356 $i.desc=$description $range"""
357 ).substitute(param, range = range, i = i), file=f)
358 print("}\n}\n# End of autogenerated section. You may edit below.", file=f)
359 f.close()
360
361 def generateusage(self):
362 self.mkdirabs("docs")
363 f = open(os.path.join(self.binary_dir, "docs", self.msgname+"-usage.dox"), 'w')
364 #print >> f, "/**"
365 print >> f, "\\subsubsection usage Usage"
366 print >> f, '\\verbatim'
367 print >> f, Template('<node name="$nodename" pkg="$pkgname" type="$nodename">').\
368 substitute(pkgname = self.pkgname, nodename = self.nodename)
369 for param in self.group.get_parameters():
370 print >> f, Template(' <param name="$name" type="$type" value="$default" />').substitute(param)
371 print >> f, '</node>'
372 print >> f, '\\endverbatim'
373 print >> f
374 #print >> f, "*/"
375 f.close()
376
377 def generatedoc(self):
378 self.mkdirabs("docs")
379 dir_path = os.path.join(self.binary_dir, "docs")
380 self.mkdirabs(dir_path)
381 f = open(os.path.join(dir_path, self.msgname+".dox"), 'w')
382 #print >> f, "/**"
383 print("\\subsubsection parameters ROS parameters", file=f)
384 print("", file=f)
385 print("Reads and maintains the following parameters on the ROS server", file=f)
386 print("", file=f)
387 for param in self.group.get_parameters():
388 print(Template("- \\b \"~$name\" : \\b [$type] $description min: $min, default: $default, max: $max").substitute(param), file=f)
389 print("", file=f)
390 #print >> f, "*/"
391 f.close()
392
393 def generateusage(self):
394 self.mkdirabs("docs")
395 f = open(os.path.join(self.binary_dir, "docs", self.msgname+"-usage.dox"), 'w')
396 #print >> f, "/**"
397 print("\\subsubsection usage Usage", file=f)
398 print('\\verbatim', file=f)
399 print(Template('<node name="$nodename" pkg="$pkgname" type="$nodename">').\
400 substitute(pkgname = self.pkgname, nodename = self.nodename), file=f)
401 for param in self.group.get_parameters():
402 print(Template(' <param name="$name" type="$type" value="$default" />').substitute(param), file=f)
403 print('</node>', file=f)
404 print('\\endverbatim', file=f)
405 print("", file=f)
406 #print >> f, "*/"
407 f.close()
408
409 def crepr(self, param, val):
410 type = param["type"]
411 if type == 'str':
412 return '"'+val+'"'
413 if type in [ 'int', 'double']:
414 return str(val)
415 if type == 'bool':
416 return { True : 1, False : 0 }[val]
417 raise TypeError(type)
418 # if type == 'string':
419 # return '"'+val+'"'
420 # if 'uint' in type:
421 # return str(val)+'ULL'
422 # if 'int' in type:
423 # return str(val)+'LL'
424 # if 'time' in type:
425 # return 'ros::Time('+str(val)+')'
426 # if 'duration' in type:
427 # return 'ros::Duration('+str(val)+')'
428 # if 'float' in types:
429 # return str(val)
430
431 def appendline(self, list, text, param, value = None):
432 if value == None:
433 val = ""
434 else:
435 val = self.crepr(param, param[value])
436 list.append(Template('${doline} $srcline "$srcfile"\n '+text).safe_substitute(param, v=val, doline=LINEDEBUG, configname=self.name))
437
438 def appendgroup(self, list, group):
439 subgroups = []
440 for g in group.groups:
441 self.appendgroup(subgroups, g)
442 setters = []
443 params = []
444 for p in group.parameters:
445 setters.append(Template(" if(\"${name}\"==(*_i)->name){${name} = boost::any_cast<${ctype}>(val);}").substitute(p));
446 params.append(Template("${ctype} ${name};").substitute(p));
447
448 subgroups = "\n".join(subgroups)
449 setters = "\n".join(setters)
450 params = "\n".join(params)
451 grouptemplate = open(os.path.join(self.dynconfpath, "templates", "GroupClass.h.template")).read()
452 list.append(Template(grouptemplate).safe_substitute(group.to_dict(), subgroups = subgroups, setters = setters, params = params, configname = self.name))
453
454 def generatecpp(self):
455 # Read the configuration manipulator template and insert line numbers and file name into template.
456 templatefile = os.path.join(self.dynconfpath, "templates", "ConfigType.h.template")
457 templatelines = []
458 templatefilesafe = templatefile.replace('\\', '\\\\') # line directive does backslash expansion.
459 curline = 1
460 f = open(templatefile)
461 for line in f:
462 curline = curline + 1
463 templatelines.append(Template(line).safe_substitute(linenum=curline,filename=templatefilesafe))
464 f.close()
465 template = ''.join(templatelines)
466
467 # Write the configuration manipulator.
468 self.mkdirabs(self.cpp_gen_dir)
469 f = open(os.path.join(self.cpp_gen_dir, self.name+"Config.h"), 'w')
470 paramdescr = []
471 groups = []
472 members = []
473 constants = []
474 for const in self.constants:
475 self.appendline(constants, "${cconsttype} ${configname}_${name} = $v;", const, "value")
476
477 def write_params(group):
478 if group.id == 0:
479 paramdescr.append(Template("${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config> ${name}(\"${name}\", \"${type}\", ${parent}, ${id}, ${cstate}, &${configname}Config::${lower});").safe_substitute(group.to_dict(), configname = self.name))
480 else:
481 paramdescr.append(Template("${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config::${parentclass}> ${name}(\"${name}\", \"${type}\", ${parent}, ${id}, ${cstate}, &${configname}Config::${field});").safe_substitute(group.to_dict(), configname = self.name))
482 for param in group.parameters:
483 self.appendline(members, "${ctype} ${name};", param)
484 self.appendline(paramdescr, "__min__.${name} = $v;", param, "min")
485 self.appendline(paramdescr, "__max__.${name} = $v;", param, "max")
486 self.appendline(paramdescr, "__default__.${name} = $v;", param, "default")
487 self.appendline(paramdescr, group.to_dict()['name']+".abstract_parameters.push_back(${configname}Config::AbstractParamDescriptionConstPtr(new ${configname}Config::ParamDescription<${ctype}>(\"${name}\", \"${type}\", ${level}, "\
488 "\"${description}\", \"${edit_method}\", &${configname}Config::${name})));", param)
489 self.appendline(paramdescr,
490 "__param_descriptions__.push_back(${configname}Config::AbstractParamDescriptionConstPtr(new ${configname}Config::ParamDescription<${ctype}>(\"${name}\", \"${type}\", ${level}, "\
491 "\"${description}\", \"${edit_method}\", &${configname}Config::${name})));", param)
492
493 for g in group.groups:
494 write_params(g)
495
496 self.appendline(paramdescr, "${name}.convertParams();", group.to_dict())
497 if group.id == 0:
498 self.appendline(paramdescr, "__group_descriptions__.push_back(${configname}Config::AbstractGroupDescriptionConstPtr(new ${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config>(${name})));", group.to_dict())
499 else:
500 self.appendline(paramdescr, "${parentname}.groups.push_back(${configname}Config::AbstractGroupDescriptionConstPtr(new ${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config::${parentclass}>(${name})));", group.to_dict())
501 self.appendline(paramdescr, "__group_descriptions__.push_back(${configname}Config::AbstractGroupDescriptionConstPtr(new ${configname}Config::GroupDescription<${configname}Config::${class}, ${configname}Config::${parentclass}>(${name})));", group.to_dict())
502
503 write_params(self.group)
504 self.appendgroup(groups, self.group)
505
506 paramdescr = '\n'.join(paramdescr)
507 members = '\n'.join(members)
508 constants = '\n'.join(constants)
509 groups = '\n'.join(groups)
510 f.write(Template(template).substitute(uname=self.name.upper(),
511 configname=self.name, pkgname = self.pkgname, paramdescr = paramdescr,
512 members = members, groups = groups, doline = LINEDEBUG, constants = constants))
513 f.close()
514 print("Wrote header file in " + os.path.join(self.cpp_gen_dir, self.name+"Config.h"))
515
516 #def deleteoneobsolete(self, file):
517 # try:
518 # os.unlink(file)
519 # except OSError:
520 # pass
521
522 # def deleteobsolete(self): ### @todo remove this after the transition period.
523 # self.deleteoneobsolete(os.path.join(self.pkgpath, "msg", self.msgname+".msg"))
524 # self.deleteoneobsolete(os.path.join("msg", "cpp", self.pkgpath, "msg", self.msgname+".msg"))
525 # self.deleteoneobsolete(os.path.join(self.pkgpath, "srv", "Get"+self.msgname+".srv"))
526 # self.deleteoneobsolete(os.path.join("srv", "cpp", self.pkgpath, "srv", "Get"+self.msgname+".srv"))
527 # self.deleteoneobsolete(os.path.join(self.pkgpath, "srv", "Set"+self.msgname+".srv"))
528 # self.deleteoneobsolete(os.path.join("srv", "cpp", self.pkgpath, "srv", "Set"+self.msgname+".srv"))
529
530 # def msgtype(self, type):
531 # return { 'int' : 'int32', 'bool' : 'int8', 'str' : 'string', 'double' : 'float64' }[type]
532 #
533 # def generatemsg(self):
534 # self.mkdir("msg")
535 # f = open(os.path.join(self.pkgpath, "msg", self.msgname+".msg"), 'w')
536 # print >> f, "# This is an autogerenated file. Please do not edit."
537 # print >> f, ""
538 # for param in self.parameters:
539 # print >> f, Template("$type $name # $description").substitute(param, type=self.msgtype(param['type']))
540 # f.close()
541 #
542 # def generategetsrv(self):
543 # self.mkdir("srv")
544 # f = open(os.path.join(self.pkgpath, "srv", "Get"+self.msgname+".srv"), 'w')
545 # print >> f, "# This is an autogerenated file. Please do not edit."
546 # print >> f, ""
547 # print >> f, "---"
548 # print >> f, self.msgname, "config", "# Current configuration of node."
549 # print >> f, self.msgname, "defaults", "# Minimum values where appropriate."
550 # print >> f, self.msgname, "min", "# Minimum values where appropriate."
551 # print >> f, self.msgname, "max", "# Maximum values where appropriate."
552 # f.close()
553 #
554 # def generatesetsrv(self):
555 # self.mkdir("srv")
556 # f = open(os.path.join(self.pkgpath, "srv", "Set"+self.msgname+".srv"), 'w')
557 # print >> f, "# This is an autogerenated file. Please do not edit."
558 # print >> f, self.msgname, "config", "# Requested node configuration."
559 # print >> f, "---"
560 # print >> f, self.msgname, "config", "# What the node's configuration was actually set to."
561 # f.close()
562
563 def generatepy(self):
564 # Read the configuration manipulator template and insert line numbers and file name into template.
565 templatefile = os.path.join(self.dynconfpath, "templates", "ConfigType.py.template")
566 templatelines = []
567 f = open(templatefile)
568 template = f.read()
569 f.close()
570
571 # Write the configuration manipulator.
572 self.mkdirabs(os.path.join(self.py_gen_dir, "cfg"))
573 f = open(os.path.join(self.py_gen_dir, "cfg", self.name+"Config.py"), 'w')
574 f.write(Template(template).substitute(name = self.name,
575 pkgname = self.pkgname, pycfgdata = self.group.to_dict()))
576 for const in self.constants:
577 f.write(Template("${configname}_${name} = $v\n").
578 substitute(const, v = repr(const['value']),
579 configname=self.name))
580 f.close()
581
582 f = open(os.path.join(self.py_gen_dir, "cfg", "__init__.py"), 'a')
583 f.close()
0 # Software License Agreement (BSD License)
1 #
2 # Copyright (c) 2009, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials provided
14 # with the distribution.
15 # * Neither the name of Willow Garage, Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31
32 """
33 Python client API for dynamic_reconfigure (L{DynamicReconfigureClient}) as well as
34 example server implementation (L{DynamicReconfigureServer}).
35 """
36
37 from __future__ import with_statement
38
39 try:
40 import roslib; roslib.load_manifest('dynamic_reconfigure')
41 except:
42 pass
43 import rospy
44 import rosservice
45 import threading
46 import time
47 import copy
48 from dynamic_reconfigure import DynamicReconfigureCallbackException
49 from dynamic_reconfigure.srv import Reconfigure as ReconfigureSrv
50 from dynamic_reconfigure.msg import Config as ConfigMsg
51 from dynamic_reconfigure.msg import ConfigDescription as ConfigDescrMsg
52 from dynamic_reconfigure.msg import IntParameter, BoolParameter, StrParameter, DoubleParameter, ParamDescription
53 from dynamic_reconfigure.encoding import *
54
55 class Server(object):
56 def __init__(self, type, callback):
57 self.mutex = threading.Lock()
58 self.type = type
59 self.config = type.defaults.copy()
60
61 self.description = encode_description(type)
62 self._copy_from_parameter_server()
63 self.callback = callback
64 self._clamp(self.config)
65
66 # setup group defaults
67 self.config['groups'] = get_tree(self.description)
68 self.config = initial_config(encode_config(self.config), type.config_description)
69
70 self.descr_topic = rospy.Publisher('~parameter_descriptions', ConfigDescrMsg, latch=True, queue_size=10)
71 self.descr_topic.publish(self.description);
72
73 self.update_topic = rospy.Publisher('~parameter_updates', ConfigMsg, latch=True, queue_size=10)
74 self._change_config(self.config, ~0) # Consistent with the C++ API, the callback gets called with level=~0 (i.e. -1)
75
76 self.set_service = rospy.Service('~set_parameters', ReconfigureSrv, self._set_callback)
77
78 def update_configuration(self, changes):
79 with self.mutex:
80 new_config = copy.deepcopy(self.config)
81 new_config.update(changes)
82 self._clamp(new_config)
83 return self._change_config(new_config, self._calc_level(new_config, self.config))
84
85 def _copy_from_parameter_server(self):
86 for param in extract_params(self.type.config_description):
87 try:
88 self.config[param['name']] = rospy.get_param("~" + param['name'])
89 except KeyError:
90 pass
91
92 def _copy_to_parameter_server(self):
93 for param in extract_params(self.type.config_description):
94 rospy.set_param('~' + param['name'], self.config[param['name']])
95
96 def _change_config(self, config, level):
97 self.config = self.callback(config, level)
98 if self.config is None:
99 msg = 'Reconfigure callback should return a possibly updated configuration.'
100 rospy.logerr(msg)
101 raise DynamicReconfigureCallbackException(msg)
102
103 self._copy_to_parameter_server()
104
105 self.update_topic.publish(encode_config(self.config))
106
107 return self.config
108
109 def _calc_level(self, config1, config2):
110 level = 0
111 for param in extract_params(self.type.config_description):
112 if config1[param['name']] != config2[param['name']]:
113 level |= param['level']
114
115 return level
116
117 def _clamp(self, config):
118 for param in extract_params(self.type.config_description):
119 maxval = self.type.max[param['name']]
120 minval = self.type.min[param['name']]
121 val = config[param['name']]
122 if val > maxval and maxval != "":
123 config[param['name']] = maxval
124 elif val < minval and minval != "":
125 config[param['name']] = minval
126
127 def _set_callback(self, req):
128 return encode_config(self.update_configuration(decode_config(req.config, self.type.config_description)))
0 #include <dynamic_reconfigure/config_init_mutex.h>
1
2 boost::mutex dynamic_reconfigure::__init_mutex__;
0 Config config
1 ---
2 Config config
0 ${doline} ${linenum} "${filename}"
1 // *********************************************************
2 //
3 // File autogenerated for the ${pkgname} package
4 // by the dynamic_reconfigure package.
5 // Please do not edit.
6 //
7 // ********************************************************/
8
9 #ifndef __${pkgname}__${uname}CONFIG_H__
10 #define __${pkgname}__${uname}CONFIG_H__
11
12 #include <dynamic_reconfigure/config_tools.h>
13 #include <limits>
14 #include <ros/node_handle.h>
15 #include <dynamic_reconfigure/ConfigDescription.h>
16 #include <dynamic_reconfigure/ParamDescription.h>
17 #include <dynamic_reconfigure/Group.h>
18 #include <dynamic_reconfigure/config_init_mutex.h>
19 #include <boost/any.hpp>
20
21 namespace ${pkgname}
22 {
23 class ${configname}ConfigStatics;
24
25 class ${configname}Config
26 {
27 public:
28 class AbstractParamDescription : public dynamic_reconfigure::ParamDescription
29 {
30 public:
31 AbstractParamDescription(std::string n, std::string t, uint32_t l,
32 std::string d, std::string e)
33 {
34 name = n;
35 type = t;
36 level = l;
37 description = d;
38 edit_method = e;
39 }
40
41 virtual void clamp(${configname}Config &config, const ${configname}Config &max, const ${configname}Config &min) const = 0;
42 virtual void calcLevel(uint32_t &level, const ${configname}Config &config1, const ${configname}Config &config2) const = 0;
43 virtual void fromServer(const ros::NodeHandle &nh, ${configname}Config &config) const = 0;
44 virtual void toServer(const ros::NodeHandle &nh, const ${configname}Config &config) const = 0;
45 virtual bool fromMessage(const dynamic_reconfigure::Config &msg, ${configname}Config &config) const = 0;
46 virtual void toMessage(dynamic_reconfigure::Config &msg, const ${configname}Config &config) const = 0;
47 virtual void getValue(const ${configname}Config &config, boost::any &val) const = 0;
48 };
49
50 typedef boost::shared_ptr<AbstractParamDescription> AbstractParamDescriptionPtr;
51 typedef boost::shared_ptr<const AbstractParamDescription> AbstractParamDescriptionConstPtr;
52
53 template <class T>
54 class ParamDescription : public AbstractParamDescription
55 {
56 public:
57 ParamDescription(std::string name, std::string type, uint32_t level,
58 std::string description, std::string edit_method, T ${configname}Config::* f) :
59 AbstractParamDescription(name, type, level, description, edit_method),
60 field(f)
61 {}
62
63 T (${configname}Config::* field);
64
65 virtual void clamp(${configname}Config &config, const ${configname}Config &max, const ${configname}Config &min) const
66 {
67 if (config.*field > max.*field)
68 config.*field = max.*field;
69
70 if (config.*field < min.*field)
71 config.*field = min.*field;
72 }
73
74 virtual void calcLevel(uint32_t &comb_level, const ${configname}Config &config1, const ${configname}Config &config2) const
75 {
76 if (config1.*field != config2.*field)
77 comb_level |= level;
78 }
79
80 virtual void fromServer(const ros::NodeHandle &nh, ${configname}Config &config) const
81 {
82 nh.getParam(name, config.*field);
83 }
84
85 virtual void toServer(const ros::NodeHandle &nh, const ${configname}Config &config) const
86 {
87 nh.setParam(name, config.*field);
88 }
89
90 virtual bool fromMessage(const dynamic_reconfigure::Config &msg, ${configname}Config &config) const
91 {
92 return dynamic_reconfigure::ConfigTools::getParameter(msg, name, config.*field);
93 }
94
95 virtual void toMessage(dynamic_reconfigure::Config &msg, const ${configname}Config &config) const
96 {
97 dynamic_reconfigure::ConfigTools::appendParameter(msg, name, config.*field);
98 }
99
100 virtual void getValue(const ${configname}Config &config, boost::any &val) const
101 {
102 val = config.*field;
103 }
104 };
105
106 class AbstractGroupDescription : public dynamic_reconfigure::Group
107 {
108 public:
109 AbstractGroupDescription(std::string n, std::string t, int p, int i, bool s)
110 {
111 name = n;
112 type = t;
113 parent = p;
114 state = s;
115 id = i;
116 }
117
118 std::vector<AbstractParamDescriptionConstPtr> abstract_parameters;
119 bool state;
120
121 virtual void toMessage(dynamic_reconfigure::Config &msg, const boost::any &config) const = 0;
122 virtual bool fromMessage(const dynamic_reconfigure::Config &msg, boost::any &config) const =0;
123 virtual void updateParams(boost::any &cfg, ${configname}Config &top) const= 0;
124 virtual void setInitialState(boost::any &cfg) const = 0;
125
126
127 void convertParams()
128 {
129 for(std::vector<AbstractParamDescriptionConstPtr>::const_iterator i = abstract_parameters.begin(); i != abstract_parameters.end(); ++i)
130 {
131 parameters.push_back(dynamic_reconfigure::ParamDescription(**i));
132 }
133 }
134 };
135
136 typedef boost::shared_ptr<AbstractGroupDescription> AbstractGroupDescriptionPtr;
137 typedef boost::shared_ptr<const AbstractGroupDescription> AbstractGroupDescriptionConstPtr;
138
139 template<class T, class PT>
140 class GroupDescription : public AbstractGroupDescription
141 {
142 public:
143 GroupDescription(std::string name, std::string type, int parent, int id, bool s, T PT::* f) : AbstractGroupDescription(name, type, parent, id, s), field(f)
144 {
145 }
146
147 GroupDescription(const GroupDescription<T, PT>& g): AbstractGroupDescription(g.name, g.type, g.parent, g.id, g.state), field(g.field), groups(g.groups)
148 {
149 parameters = g.parameters;
150 abstract_parameters = g.abstract_parameters;
151 }
152
153 virtual bool fromMessage(const dynamic_reconfigure::Config &msg, boost::any &cfg) const
154 {
155 PT* config = boost::any_cast<PT*>(cfg);
156 if(!dynamic_reconfigure::ConfigTools::getGroupState(msg, name, (*config).*field))
157 return false;
158
159 for(std::vector<AbstractGroupDescriptionConstPtr>::const_iterator i = groups.begin(); i != groups.end(); ++i)
160 {
161 boost::any n = &((*config).*field);
162 if(!(*i)->fromMessage(msg, n))
163 return false;
164 }
165
166 return true;
167 }
168
169 virtual void setInitialState(boost::any &cfg) const
170 {
171 PT* config = boost::any_cast<PT*>(cfg);
172 T* group = &((*config).*field);
173 group->state = state;
174
175 for(std::vector<AbstractGroupDescriptionConstPtr>::const_iterator i = groups.begin(); i != groups.end(); ++i)
176 {
177 boost::any n = boost::any(&((*config).*field));
178 (*i)->setInitialState(n);
179 }
180
181 }
182
183 virtual void updateParams(boost::any &cfg, ${configname}Config &top) const
184 {
185 PT* config = boost::any_cast<PT*>(cfg);
186
187 T* f = &((*config).*field);
188 f->setParams(top, abstract_parameters);
189
190 for(std::vector<AbstractGroupDescriptionConstPtr>::const_iterator i = groups.begin(); i != groups.end(); ++i)
191 {
192 boost::any n = &((*config).*field);
193 (*i)->updateParams(n, top);
194 }
195 }
196
197 virtual void toMessage(dynamic_reconfigure::Config &msg, const boost::any &cfg) const
198 {
199 const PT config = boost::any_cast<PT>(cfg);
200 dynamic_reconfigure::ConfigTools::appendGroup<T>(msg, name, id, parent, config.*field);
201
202 for(std::vector<AbstractGroupDescriptionConstPtr>::const_iterator i = groups.begin(); i != groups.end(); ++i)
203 {
204 (*i)->toMessage(msg, config.*field);
205 }
206 }
207
208 T (PT::* field);
209 std::vector<${configname}Config::AbstractGroupDescriptionConstPtr> groups;
210 };
211
212 ${groups}
213
214
215 ${members}
216 ${doline} ${linenum} "${filename}"
217
218 bool __fromMessage__(dynamic_reconfigure::Config &msg)
219 {
220 const std::vector<AbstractParamDescriptionConstPtr> &__param_descriptions__ = __getParamDescriptions__();
221 const std::vector<AbstractGroupDescriptionConstPtr> &__group_descriptions__ = __getGroupDescriptions__();
222
223 int count = 0;
224 for (std::vector<AbstractParamDescriptionConstPtr>::const_iterator i = __param_descriptions__.begin(); i != __param_descriptions__.end(); ++i)
225 if ((*i)->fromMessage(msg, *this))
226 count++;
227
228 for (std::vector<AbstractGroupDescriptionConstPtr>::const_iterator i = __group_descriptions__.begin(); i != __group_descriptions__.end(); i ++)
229 {
230 if ((*i)->id == 0)
231 {
232 boost::any n = boost::any(this);
233 (*i)->updateParams(n, *this);
234 (*i)->fromMessage(msg, n);
235 }
236 }
237
238 if (count != dynamic_reconfigure::ConfigTools::size(msg))
239 {
240 ROS_ERROR("${configname}Config::__fromMessage__ called with an unexpected parameter.");
241 ROS_ERROR("Booleans:");
242 for (unsigned int i = 0; i < msg.bools.size(); i++)
243 ROS_ERROR(" %s", msg.bools[i].name.c_str());
244 ROS_ERROR("Integers:");
245 for (unsigned int i = 0; i < msg.ints.size(); i++)
246 ROS_ERROR(" %s", msg.ints[i].name.c_str());
247 ROS_ERROR("Doubles:");
248 for (unsigned int i = 0; i < msg.doubles.size(); i++)
249 ROS_ERROR(" %s", msg.doubles[i].name.c_str());
250 ROS_ERROR("Strings:");
251 for (unsigned int i = 0; i < msg.strs.size(); i++)
252 ROS_ERROR(" %s", msg.strs[i].name.c_str());
253 // @todo Check that there are no duplicates. Make this error more
254 // explicit.
255 return false;
256 }
257 return true;
258 }
259
260 // This version of __toMessage__ is used during initialization of
261 // statics when __getParamDescriptions__ can't be called yet.
262 void __toMessage__(dynamic_reconfigure::Config &msg, const std::vector<AbstractParamDescriptionConstPtr> &__param_descriptions__, const std::vector<AbstractGroupDescriptionConstPtr> &__group_descriptions__) const
263 {
264 dynamic_reconfigure::ConfigTools::clear(msg);
265 for (std::vector<AbstractParamDescriptionConstPtr>::const_iterator i = __param_descriptions__.begin(); i != __param_descriptions__.end(); ++i)
266 (*i)->toMessage(msg, *this);
267
268 for (std::vector<AbstractGroupDescriptionConstPtr>::const_iterator i = __group_descriptions__.begin(); i != __group_descriptions__.end(); ++i)
269 {
270 if((*i)->id == 0)
271 {
272 (*i)->toMessage(msg, *this);
273 }
274 }
275 }
276
277 void __toMessage__(dynamic_reconfigure::Config &msg) const
278 {
279 const std::vector<AbstractParamDescriptionConstPtr> &__param_descriptions__ = __getParamDescriptions__();
280 const std::vector<AbstractGroupDescriptionConstPtr> &__group_descriptions__ = __getGroupDescriptions__();
281 __toMessage__(msg, __param_descriptions__, __group_descriptions__);
282 }
283
284 void __toServer__(const ros::NodeHandle &nh) const
285 {
286 const std::vector<AbstractParamDescriptionConstPtr> &__param_descriptions__ = __getParamDescriptions__();
287 for (std::vector<AbstractParamDescriptionConstPtr>::const_iterator i = __param_descriptions__.begin(); i != __param_descriptions__.end(); ++i)
288 (*i)->toServer(nh, *this);
289 }
290
291 void __fromServer__(const ros::NodeHandle &nh)
292 {
293 static bool setup=false;
294
295 const std::vector<AbstractParamDescriptionConstPtr> &__param_descriptions__ = __getParamDescriptions__();
296 for (std::vector<AbstractParamDescriptionConstPtr>::const_iterator i = __param_descriptions__.begin(); i != __param_descriptions__.end(); ++i)
297 (*i)->fromServer(nh, *this);
298
299 const std::vector<AbstractGroupDescriptionConstPtr> &__group_descriptions__ = __getGroupDescriptions__();
300 for (std::vector<AbstractGroupDescriptionConstPtr>::const_iterator i = __group_descriptions__.begin(); i != __group_descriptions__.end(); i++){
301 if (!setup && (*i)->id == 0) {
302 setup = true;
303 boost::any n = boost::any(this);
304 (*i)->setInitialState(n);
305 }
306 }
307 }
308
309 void __clamp__()
310 {
311 const std::vector<AbstractParamDescriptionConstPtr> &__param_descriptions__ = __getParamDescriptions__();
312 const ${configname}Config &__max__ = __getMax__();
313 const ${configname}Config &__min__ = __getMin__();
314 for (std::vector<AbstractParamDescriptionConstPtr>::const_iterator i = __param_descriptions__.begin(); i != __param_descriptions__.end(); ++i)
315 (*i)->clamp(*this, __max__, __min__);
316 }
317
318 uint32_t __level__(const ${configname}Config &config) const
319 {
320 const std::vector<AbstractParamDescriptionConstPtr> &__param_descriptions__ = __getParamDescriptions__();
321 uint32_t level = 0;
322 for (std::vector<AbstractParamDescriptionConstPtr>::const_iterator i = __param_descriptions__.begin(); i != __param_descriptions__.end(); ++i)
323 (*i)->calcLevel(level, config, *this);
324 return level;
325 }
326
327 static const dynamic_reconfigure::ConfigDescription &__getDescriptionMessage__();
328 static const ${configname}Config &__getDefault__();
329 static const ${configname}Config &__getMax__();
330 static const ${configname}Config &__getMin__();
331 static const std::vector<AbstractParamDescriptionConstPtr> &__getParamDescriptions__();
332 static const std::vector<AbstractGroupDescriptionConstPtr> &__getGroupDescriptions__();
333
334 private:
335 static const ${configname}ConfigStatics *__get_statics__();
336 };
337
338 template <> // Max and min are ignored for strings.
339 inline void ${configname}Config::ParamDescription<std::string>::clamp(${configname}Config &config, const ${configname}Config &max, const ${configname}Config &min) const
340 {
341 return;
342 }
343
344 class ${configname}ConfigStatics
345 {
346 friend class ${configname}Config;
347
348 ${configname}ConfigStatics()
349 {
350 ${paramdescr}
351 ${doline} ${linenum} "${filename}"
352
353 for (std::vector<${configname}Config::AbstractGroupDescriptionConstPtr>::const_iterator i = __group_descriptions__.begin(); i != __group_descriptions__.end(); ++i)
354 {
355 __description_message__.groups.push_back(**i);
356 }
357 __max__.__toMessage__(__description_message__.max, __param_descriptions__, __group_descriptions__);
358 __min__.__toMessage__(__description_message__.min, __param_descriptions__, __group_descriptions__);
359 __default__.__toMessage__(__description_message__.dflt, __param_descriptions__, __group_descriptions__);
360 }
361 std::vector<${configname}Config::AbstractParamDescriptionConstPtr> __param_descriptions__;
362 std::vector<${configname}Config::AbstractGroupDescriptionConstPtr> __group_descriptions__;
363 ${configname}Config __max__;
364 ${configname}Config __min__;
365 ${configname}Config __default__;
366 dynamic_reconfigure::ConfigDescription __description_message__;
367
368 static const ${configname}ConfigStatics *get_instance()
369 {
370 // Split this off in a separate function because I know that
371 // instance will get initialized the first time get_instance is
372 // called, and I am guaranteeing that get_instance gets called at
373 // most once.
374 static ${configname}ConfigStatics instance;
375 return &instance;
376 }
377 };
378
379 inline const dynamic_reconfigure::ConfigDescription &${configname}Config::__getDescriptionMessage__()
380 {
381 return __get_statics__()->__description_message__;
382 }
383
384 inline const ${configname}Config &${configname}Config::__getDefault__()
385 {
386 return __get_statics__()->__default__;
387 }
388
389 inline const ${configname}Config &${configname}Config::__getMax__()
390 {
391 return __get_statics__()->__max__;
392 }
393
394 inline const ${configname}Config &${configname}Config::__getMin__()
395 {
396 return __get_statics__()->__min__;
397 }
398
399 inline const std::vector<${configname}Config::AbstractParamDescriptionConstPtr> &${configname}Config::__getParamDescriptions__()
400 {
401 return __get_statics__()->__param_descriptions__;
402 }
403
404 inline const std::vector<${configname}Config::AbstractGroupDescriptionConstPtr> &${configname}Config::__getGroupDescriptions__()
405 {
406 return __get_statics__()->__group_descriptions__;
407 }
408
409 inline const ${configname}ConfigStatics *${configname}Config::__get_statics__()
410 {
411 const static ${configname}ConfigStatics *statics;
412
413 if (statics) // Common case
414 return statics;
415
416 boost::mutex::scoped_lock lock(dynamic_reconfigure::__init_mutex__);
417
418 if (statics) // In case we lost a race.
419 return statics;
420
421 statics = ${configname}ConfigStatics::get_instance();
422
423 return statics;
424 }
425
426 ${constants}
427 }
428
429 #endif // __${uname}RECONFIGURATOR_H__
0 ## *********************************************************
1 ##
2 ## File autogenerated for the ${pkgname} package
3 ## by the dynamic_reconfigure package.
4 ## Please do not edit.
5 ##
6 ## ********************************************************/
7
8 from dynamic_reconfigure.encoding import extract_params
9
10 inf = float('inf')
11
12 config_description = ${pycfgdata}
13
14 min = {}
15 max = {}
16 defaults = {}
17 level = {}
18 type = {}
19 all_level = 0
20
21 #def extract_params(config):
22 # params = []
23 # params.extend(config['parameters'])
24 # for group in config['groups']:
25 # params.extend(extract_params(group))
26 # return params
27
28 for param in extract_params(config_description):
29 min[param['name']] = param['min']
30 max[param['name']] = param['max']
31 defaults[param['name']] = param['default']
32 level[param['name']] = param['level']
33 type[param['name']] = param['type']
34 all_level = all_level | param['level']
35
0 class ${upper}
1 {
2 public:
3 ${upper}()
4 {
5 state = true;
6 name = "${name}";
7 }
8
9 void setParams(${configname}Config &config, const std::vector<AbstractParamDescriptionConstPtr> params)
10 {
11 for (std::vector<AbstractParamDescriptionConstPtr>::const_iterator _i = params.begin(); _i != params.end(); ++_i)
12 {
13 boost::any val;
14 (*_i)->getValue(config, val);
15
16 ${setters}
17 }
18 }
19
20 ${params}
21
22 bool state;
23 std::string name;
24
25 ${subgroups}
26 }${lower};
0 add_executable(dynamic_reconfigure-ref_server EXCLUDE_FROM_ALL ref_server.cpp)
1 add_dependencies(dynamic_reconfigure-ref_server ${PROJECT_NAME}_gencfg ${PROJECT_NAME}_generate_messages_cpp ${PROJECT_NAME}_generate_messages_py)
2 target_link_libraries(dynamic_reconfigure-ref_server pthread dynamic_reconfigure_config_init_mutex ${catkin_LIBRARIES})
3
4 add_dependencies(tests dynamic_reconfigure-ref_server)
5
6 add_rostest(test_python_simple_client.launch)
0 /*
1 * Copyright (c) 2014, Open Source Robotics Foundation, Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the Open Source Robotics Foundation, Inc. nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29
30 #include <ros/ros.h>
31
32 #include <dynamic_reconfigure/server.h>
33 #include <dynamic_reconfigure/TestConfig.h>
34
35 void callback(dynamic_reconfigure_test::TestConfig &config, uint32_t level) {
36 ROS_INFO("Reconfigure request: %i %f %s %s %i %i Group1: %i Group2: %f %s",
37 config.int_, config.double_, config.str_.c_str(), config.mstr_.c_str(),
38 (int) config.bool_, config.level, config.group1_int,
39 config.group2_double, config.group2_string.c_str());
40 }
41
42 int main(int argc, char **argv) {
43 ros::init(argc, argv, "dynamic_reconfigure_test");
44
45 dynamic_reconfigure::Server<dynamic_reconfigure_test::TestConfig> server;
46 dynamic_reconfigure::Server<dynamic_reconfigure_test::TestConfig>::CallbackType f;
47
48 f = boost::bind(&callback, _1, _2);
49 server.setCallback(f);
50
51 ROS_INFO("Spinning node");
52 ros::spin();
53 return 0;
54 }
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # Software License Agreement (BSD License)
3 #
4 # Copyright (c) 2014, Open Source Robotics Foundation, Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials provided
16 # with the distribution.
17 # * Neither the name of Open Source Robotics Foundation, Inc. nor the
18 # names of its contributors may be used to endorse or promote products
19 # derived from this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33
34 import unittest
35 import rospy
36 import dynamic_reconfigure.client
37
38 class TestSimpleDynamicReconfigureClient(unittest.TestCase):
39
40 def testsimple(self):
41 client = dynamic_reconfigure.client.Client("ref_server", timeout=5)
42 config = client.get_configuration(timeout=5)
43 self.assertEqual(0, config['int_'])
44 self.assertEqual(0.0, config['double_'])
45 self.assertEqual('foo', config['str_'])
46 self.assertEqual(False, config['bool_'])
47
48 int_ = 7
49 double_ = 0.75
50 str_ = 'bar'
51 bool_ = True
52
53 client.update_configuration(
54 {"int_": int_, "double_": double_, "str_": str_,
55 "bool_": bool_}
56 )
57
58 rospy.sleep(1.0)
59
60 config = client.get_configuration(timeout=5)
61
62 self.assertEqual(int_, config['int_'])
63 self.assertEqual(double_, config['double_'])
64 self.assertEqual(str_, config['str_'])
65 self.assertEqual(type(str_), type(config['str_']))
66 self.assertEqual(bool_, config['bool_'])
67
68 def testmultibytestring(self):
69 client = dynamic_reconfigure.client.Client("ref_server", timeout=5)
70 config = client.get_configuration(timeout=5)
71 self.assertEqual('bar', config['mstr_'])
72
73 # Kanji for konnichi wa (hello)
74 str_ = u"今日は"
75
76 client.update_configuration(
77 {"mstr_": str_}
78 )
79
80 rospy.sleep(1.0)
81
82 config = client.get_configuration(timeout=5)
83
84 self.assertEqual(u"今日は", config['mstr_'])
85 self.assertEqual(type(u"今日は"), type(config['mstr_']))
86 self.assertEqual(u"今日は", rospy.get_param('/ref_server/mstr_'))
87
88 # Hiragana for konnichi wa (hello)
89 str_ = u"こんにちは"
90
91 client.update_configuration(
92 {"mstr_": str_}
93 )
94
95 rospy.sleep(1.0)
96
97 config = client.get_configuration(timeout=5)
98
99 self.assertEqual(u"こんにちは", config['mstr_'])
100 self.assertEqual(type(u"こんにちは"), type(config['mstr_']))
101 self.assertEqual(u"こんにちは", rospy.get_param('/ref_server/mstr_'))
102
103 if __name__ == "__main__":
104 import rostest
105 rospy.init_node('simple_python_client_test')
106 rostest.rosrun('dynamic_reconfigure', 'test_simple_dynamic_reconfigure_client_python', TestSimpleDynamicReconfigureClient)
0 <launch>
1 </launch>
0 <launch>
1 <node pkg="dynamic_reconfigure" type="dynamic_reconfigure-ref_server" name="ref_server" output="screen" />
2
3 <test test-name="simple_python_client_test" pkg="dynamic_reconfigure" type="simple_python_client_test.py" />
4 </launch>
0 #!/usr/bin/env python
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2009, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32
33 import roslib; roslib.load_manifest('dynamic_reconfigure')
34 import rospy
35 from dynamic_reconfigure.client import Client as DynamicReconfigureClient
36 import time
37
38 # This example assumes that testserver in dynamic_reconfigure is running
39 # with its default node name, and will show how to reconfigure it from
40 # python.
41 #
42 # Note that testserver often changes the parameters it is given, so the
43 # values you get back from it will often be different from the ones you
44 # requested. Look at test/testserver.cpp to understand the changes that are
45 # being made to the parameters.
46 #
47 # In one window do:
48 # rosrun dynamic_reconfigure testserver
49 #
50 # In another window do:
51 # rosrun dynamic_reconfigure testclient.py
52
53 def print_config(config):
54 for k, v in config.iteritems():
55 print k, ":", v
56 print
57
58 # The config_callback (introduced below) receives a dictionary containing
59 # the current configuration of the server each time the server's
60 # configuration changes.
61 def config_callback(config):
62 print "Got callback, configuration is: "
63 print_config(config)
64
65 def new_config_callback(config):
66 global old_callback
67 print "New callback is calling old callback..."
68 old_callback(config)
69 print "New callback is done..."
70 print
71
72 # First you need to connect to the server. You can optionally specify a
73 # timeout and a config_callback that is called each time the server's
74 # configuration changes. If you do not indicate a timeout, the client is
75 # willing to wait forever for the server to be available.
76 #
77 # Note that the config_callback could get called before the constructor
78 # returns.
79 rospy.init_node('testclient_py', anonymous=True)
80 client = DynamicReconfigureClient('/dynamic_reconfigure_test_server', config_callback=config_callback, timeout=5)
81 time.sleep(1)
82
83 # You can also get the configuration manually by calling get_configuration.
84 print "Configuration from get_configuration:"
85 print_config(client.get_configuration(timeout=5))
86 time.sleep(1)
87
88 # You can push changes to the server using the update_configuration method.
89 # You can set any subset of the node's parameters using this method. It
90 # returns out the full new configuration of the server (which may differ
91 # from what you requested if you asked for something illegal).
92 print "Configuration after setting int_ to 4:"
93 print_config(client.update_configuration({'int_' : 4}))
94 time.sleep(1)
95
96 print "Configuration after setting int_ to 0 and bool_ to True:"
97 print_config(client.update_configuration({'int_' : 0, 'bool_' : True}))
98 time.sleep(1)
99
100 # You can access constants defined in Test.cfg file in the following way:
101 import dynamic_reconfigure.cfg.TestConfig as Config
102 print "Medium is a constant that is set to 1:", Config.Test_Medium
103
104 # This is useful for setting enums:
105 print "Configuration after setting int_enum_ to Medium:"
106 print_config(client.update_configuration({'int_enum_' : Config.Test_Medium}))
107 time.sleep(1)
108
109 # You can use the get_config_callback and set_config_callback methods to
110 # get the current callback or change the callback. It is sometimes useful
111 # not to set the callback in the constructor, but to wait until more
112 # initialization has been done first. Again, the callback will be called
113 # immediately if a configuration is available.
114 old_callback = client.get_config_callback()
115 client.set_config_callback(new_config_callback)
116
117 time.sleep(1)
118
119 # When you are done, you can close the client.
120 client.close()
0 /*********************************************************************
1 * Software License Agreement (BSD License)
2 *
3 * Copyright (c) 2009-2010, Willow Garage, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
16 * * Neither the name of the Willow Garage nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *********************************************************************/
33
34 #include <dynamic_reconfigure/server.h>
35 #include <dynamic_reconfigure/TestConfig.h>
36
37 void callback(dynamic_reconfigure::Server<dynamic_reconfigure::TestConfig>& srv, dynamic_reconfigure::TestConfig &config, uint32_t level)
38 {
39 ROS_INFO("Reconfigure request : %i %f %s %i %i Group1:%i Group2: %f %s", config.int_, config.double_, config.str_.c_str(), (int) config.bool_, config.level, config.group1_int, config.group2_double, config.group2_string.c_str());
40
41 config.int_ |= 1;
42 config.double_ = -config.double_;
43 config.str_ += "A";
44 config.bool_ = !config.bool_;
45 config.level = level;
46 dynamic_reconfigure::TestConfig max;
47 srv.getConfigMax(max);
48 max.int_ = max.int_ + 1;
49 srv.setConfigMax(max);
50 ROS_INFO("TEST");
51 ROS_INFO("Group 2 requested to be set to %d", config.groups.group_one.group2.state);
52 ROS_INFO("group2_doube requested to be set to %f", config.groups.group_one.group2.group2_double);
53
54 ROS_INFO("Reconfigured to : %i %f %s %i %i", config.int_, config.double_, config.str_.c_str(), (int) config.bool_, config.level);
55 }
56
57 int main(int argc, char **argv)
58 {
59 ros::init(argc, argv, "dynamic_reconfigure_test_server");
60
61 dynamic_reconfigure::Server<dynamic_reconfigure::TestConfig> srvs;
62 dynamic_reconfigure::Server<dynamic_reconfigure::TestConfig>::CallbackType f = boost::bind(&callback, boost::ref(srvs),_1, _2);
63 srvs.setCallback(f);
64 ROS_INFO("Constants are: %i %f %s %i", dynamic_reconfigure::Test_int_const, dynamic_reconfigure::Test_double_const, dynamic_reconfigure::Test_str_const, (int) dynamic_reconfigure::Test_bool_const);
65 ROS_INFO("Starting to spin...");
66 ros::spin();
67 return 0;
68 }
0 #! /usr/bin/env python
1 #*********************************************************************
2 # Software License Agreement (BSD License)
3 #
4 # Copyright (c) 2009-2010, Willow Garage, Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials provided
16 # with the distribution.
17 # * Neither the name of the Willow Garage nor the names of its
18 # contributors may be used to endorse or promote products derived
19 # from this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 #********************************************************************/
34
35 import roslib; roslib.load_manifest('dynamic_reconfigure')
36 import rospy
37 import dynamic_reconfigure.server
38 from dynamic_reconfigure.cfg import TestConfig
39 import time
40
41 def main():
42 rospy.init_node("python_test_server")
43 dynamic_reconfigure.server.Server(TestConfig, reconfigure)
44 while not rospy.is_shutdown():
45 time.sleep(0.1)
46
47 def reconfigure(config, level):
48 print config
49 rospy.loginfo("Reconfigure request : %i %f %s %s %i"%(config['int_'], config['double_'], config['str_'], config['bool_'], config['level']))
50
51 config['int_'] |= 1;
52 config['double_'] = -config['double_'];
53 config['str_'] += "A";
54 config['bool_'] = not config['bool_'];
55 config['level'] = level;
56
57 rospy.loginfo("Reconfigured to : %i %f %s %s %i"%(config['int_'], config['double_'], config['str_'], config['bool_'], config['level']))
58
59 return config # Returns the updated configuration.
60
61
62 if __name__ == '__main__':
63 main()
0 /*********************************************************************
1 * Software License Agreement (BSD License)
2 *
3 * Copyright (c) 2009-2010, Willow Garage, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
16 * * Neither the name of the Willow Garage nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *********************************************************************/
33
34 // This is here to confirm that including from multiple source files works.
35 #include <dynamic_reconfigure/TestConfig.h>