1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 """
34 Python client API for dynamic_reconfigure (L{DynamicReconfigureClient}) as well as
35 example server implementation (L{DynamicReconfigureServer}).
36 """
37
38 from __future__ import with_statement
39
40 import roslib; roslib.load_manifest('dynamic_reconfigure')
41 import rospy
42 import rosservice
43 import sys
44 import threading
45 import time
46 from dynamic_reconfigure import DynamicReconfigureParameterException
47 from dynamic_reconfigure.srv import Reconfigure as ReconfigureSrv
48 from dynamic_reconfigure.msg import Config as ConfigMsg
49 from dynamic_reconfigure.msg import ConfigDescription as ConfigDescrMsg
50 from dynamic_reconfigure.msg import IntParameter, BoolParameter, StrParameter, DoubleParameter, ParamDescription
51 from dynamic_reconfigure.encoding import *
52
54 """
55 Python dynamic_reconfigure client API
56 """
57 - def __init__(self, name, timeout=None, config_callback=None, description_callback=None):
58 """
59 Connect to dynamic_reconfigure server and return a client object
60
61 @param name: name of the server to connect to (usually the node name)
62 @type name: str
63 @param timeout: time to wait before giving up
64 @type timeout: float
65 @param config_callback: callback for server parameter changes
66 @param description_callback: internal use only as the API has not stabilized
67 """
68 self.name = name
69 self.config = None
70 self.param_description = None
71
72 self._param_types = None
73
74 self._cv = threading.Condition()
75
76 self._config_callback = config_callback
77 self._description_callback = description_callback
78
79 self._set_service = self._get_service_proxy('set_parameters', timeout)
80 self._updates_sub = self._get_subscriber('parameter_updates', ConfigMsg, self._updates_msg)
81 self._descriptions_sub = self._get_subscriber('parameter_descriptions', ConfigDescrMsg, self._descriptions_msg)
82
84 """
85 Return the latest received server configuration (wait to receive
86 one if none have been received)
87
88 @param timeout: time to wait before giving up
89 @type timeout: float
90 @return: dictionary mapping parameter names to values or None if unable to retrieve config.
91 @rtype: {str: value}
92 """
93 if timeout is None or timeout == 0.0:
94 if self.get_configuration(timeout=1.0) is None:
95 print >> sys.stderr, 'Waiting for configuration...'
96
97 with self._cv:
98 while self.config is None:
99 if rospy.is_shutdown():
100 return None
101 self._cv.wait()
102 else:
103 start_time = time.time()
104 with self._cv:
105 while self.config is None:
106 if rospy.is_shutdown():
107 return None
108 secs_left = timeout - (time.time() - start_time)
109 if secs_left <= 0.0:
110 break
111 self._cv.wait(secs_left)
112
113 return self.config
114
116 """
117 UNSTABLE. Return a description of the parameters for the server.
118 Do not use this method as the type that is returned may change.
119
120 @param timeout: time to wait before giving up
121 @type timeout: float
122 """
123 if timeout is None or timeout == 0.0:
124 with self._cv:
125 while self.param_description is None:
126 if rospy.is_shutdown():
127 return None
128 self._cv.wait()
129 else:
130 start_time = time.time()
131 with self._cv:
132 while self.param_description is None:
133 if rospy.is_shutdown():
134 return None
135 secs_left = timeout - (time.time() - start_time)
136 if secs_left <= 0.0:
137 break
138 self._cv.wait(secs_left)
139
140 return self.param_description
141
143 """
144 Change the server's configuration
145
146 @param changes: dictionary of key value pairs for the parameters that are changing
147 @type changes: {str: value}
148 """
149
150 if self.param_description is None:
151 self.get_parameter_descriptions()
152
153
154 if self.param_description is not None:
155 for name, value in changes.items()[:]:
156 dest_type = self._param_types.get(name)
157 if dest_type is None:
158 raise DynamicReconfigureParameterException('don\'t know parameter: %s' % name)
159
160 try:
161 changes[name] = dest_type(value)
162 except ValueError, e:
163 raise DynamicReconfigureParameterException('can\'t set parameter \'%s\' of %s: %s' % (name, str(dest_type), e))
164
165 config = encode_config(changes)
166 msg = self._set_service(config).config
167 resp = decode_config(msg)
168
169 return resp
170
172 """
173 Close connections to the server
174 """
175 self._updates_sub.unregister()
176 self._descriptions_sub.unregister()
177
178
179
181 """
182 Retrieve the config_callback
183 """
184 return self._config_callback
185
187 """
188 Set the config_callback
189 """
190 self._config_callback = value
191 if self._config_callback is not None:
192 self._config_callback(self.config)
193
194 config_callback = property(get_config_callback, set_config_callback)
195
196
197
199 """
200 Get the current description_callback
201 """
202 return self._config_callback
203
205 """
206 UNSTABLE. Set the description callback. Do not use as the type of the
207 description callback may change.
208 """
209 self._description_callback = value
210 if self._description_callback is not None:
211 self._description_callback(self.param_description)
212
213 description_callback = property(get_description_callback, set_description_callback)
214
215
216
218 service_name = rospy.resolve_name(self.name + '/' + suffix)
219 if timeout is None or timeout == 0.0:
220 try:
221 rospy.wait_for_service(service_name, 1.0)
222 except rospy.exceptions.ROSException:
223 print >> sys.stderr, 'Waiting for service %s...' % service_name
224 rospy.wait_for_service(service_name, timeout)
225 else:
226 rospy.wait_for_service(service_name, timeout)
227
228 return rospy.ServiceProxy(service_name, ReconfigureSrv)
229
231 topic_name = rospy.resolve_name(self.name + '/' + suffix)
232
233 return rospy.Subscriber(topic_name, type, callback=callback)
234
236 self.config = decode_config(msg)
237
238 with self._cv:
239 self._cv.notifyAll()
240 if self._config_callback is not None:
241 self._config_callback(self.config)
242
244 self.param_description = decode_description(msg)
245
246
247 self._param_types = {}
248 for p in self.param_description:
249 n, t = p.get('name'), p.get('type')
250 if n is not None and t is not None:
251 self._param_types[n] = self._param_type_from_string(t)
252
253 with self._cv:
254 self._cv.notifyAll()
255 if self._description_callback is not None:
256 self._description_callback(self.param_description)
257
259 if type_str == 'int': return int
260 elif type_str == 'double': return float
261 elif type_str == 'str': return str
262 elif type_str == 'bool': return bool
263 else:
264 raise DynamicReconfigureParameterException('parameter has unknown type: %s. This is a bug in dynamic_reconfigure.' % type_str)
265