Imported Upstream version 1.5.39
Leopold Palomo-Avellaneda
9 years ago
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 | BoolParameter[] bools | |
1 | IntParameter[] ints | |
2 | StrParameter[] strs | |
3 | DoubleParameter[] doubles | |
4 | GroupState[] groups |
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 | <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 | #! /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 | ${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 | <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 | ||
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 | ||
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> |