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 try:
41 import roslib; roslib.load_manifest('dynamic_reconfigure')
42 except:
43 pass
44 import rospy
45 import rosservice
46 import sys
47 import threading
48 import time
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
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
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
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
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
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
174 if self.param_description is None:
175 self.get_parameter_descriptions()
176
177
178 if self.param_description is not None:
179 for name, value in 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
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
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 changes[name] = dest_type(value)
204
205 except ValueError, e:
206 raise DynamicReconfigureParameterException('can\'t set parameter \'%s\' of %s: %s' % (name, str(dest_type), e))
207
208 if 'groups' in changes.keys():
209 changes['groups'] = self.update_groups(changes['groups'])
210
211 config = encode_config(changes)
212 msg = self._set_service(config).config
213 if self.group_description is None:
214 self.get_group_descriptions()
215 resp = decode_config(msg, self.group_description)
216
217 return resp
218
220 """
221 Changes the servers group configuration
222
223 @param changes: dictionary of key value pairs for the parameters that are changing
224 @type changes: {str: value}
225 """
226
227 descr = self.get_group_descriptions()
228
229 groups = []
230 def update_state(group, description):
231 for p,g in enumerate(description['groups']):
232 if g['name'] == group:
233 description['groups'][p]['state'] = changes[group]
234 else:
235 update_state(group, g)
236 return description
237
238 for change in changes:
239 descr = update_state(change, descr)
240
241 return descr
242
244 """
245 Close connections to the server
246 """
247 self._descriptions_sub.unregister()
248 self._updates_sub.unregister()
249
250
251
253 """
254 Retrieve the config_callback
255 """
256 return self._config_callback
257
259 """
260 Set the config_callback
261 """
262 self._config_callback = value
263 if self._config_callback is not None:
264 self._config_callback(self.config)
265
266 config_callback = property(get_config_callback, set_config_callback)
267
268
269
271 """
272 Get the current description_callback
273 """
274 return self._config_callback
275
277 """
278 UNSTABLE. Set the description callback. Do not use as the type of the
279 description callback may change.
280 """
281 self._description_callback = value
282 if self._description_callback is not None:
283 self._description_callback(self.param_description)
284
285 description_callback = property(get_description_callback, set_description_callback)
286
287
288
290 service_name = rospy.resolve_name(self.name + '/' + suffix)
291 if timeout is None or timeout == 0.0:
292 try:
293 rospy.wait_for_service(service_name, 1.0)
294 except rospy.exceptions.ROSException:
295 print >> sys.stderr, 'Waiting for service %s...' % service_name
296 rospy.wait_for_service(service_name, timeout)
297 else:
298 rospy.wait_for_service(service_name, timeout)
299
300 return rospy.ServiceProxy(service_name, ReconfigureSrv)
301
303 topic_name = rospy.resolve_name(self.name + '/' + suffix)
304
305 return rospy.Subscriber(topic_name, type, callback=callback)
306
308 if self.group_description is None:
309 self.get_group_descriptions()
310 self.config = decode_config(msg, self.group_description)
311
312 with self._cv:
313 self._cv.notifyAll()
314 if self._config_callback is not None:
315 self._config_callback(self.config)
316
318 self.group_description = decode_description(msg)
319 self.param_description = extract_params(self.group_description)
320
321
322 self._param_types = {}
323 for p in self.param_description:
324 n, t = p.get('name'), p.get('type')
325 if n is not None and t is not None:
326 self._param_types[n] = self._param_type_from_string(t)
327
328 with self._cv:
329 self._cv.notifyAll()
330 if self._description_callback is not None:
331 self._description_callback(self.param_description)
332
334 if type_str == 'int': return int
335 elif type_str == 'double': return float
336 elif type_str == 'str': return str
337 elif type_str == 'bool': return bool
338 else:
339 raise DynamicReconfigureParameterException('parameter has unknown type: %s. This is a bug in dynamic_reconfigure.' % type_str)
340