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 self.group_description = None
72
73 self._param_types = None
74
75 self._cv = threading.Condition()
76
77 self._config_callback = config_callback
78 self._description_callback = description_callback
79
80 self._set_service = self._get_service_proxy('set_parameters', timeout)
81 self._descriptions_sub = self._get_subscriber('parameter_descriptions', ConfigDescrMsg, self._descriptions_msg)
82 self._updates_sub = self._get_subscriber('parameter_updates', ConfigMsg, self._updates_msg)
83
85 """
86 Return the latest received server configuration (wait to receive
87 one if none have been received)
88
89 @param timeout: time to wait before giving up
90 @type timeout: float
91 @return: dictionary mapping parameter names to values or None if unable to retrieve config.
92 @rtype: {str: value}
93 """
94 if timeout is None or timeout == 0.0:
95 if self.get_configuration(timeout=1.0) is None:
96 print >> sys.stderr, 'Waiting for configuration...'
97
98 with self._cv:
99 while self.config is None:
100 if rospy.is_shutdown():
101 return None
102 self._cv.wait()
103 else:
104 start_time = time.time()
105 with self._cv:
106 while self.config is None:
107 if rospy.is_shutdown():
108 return None
109 secs_left = timeout - (time.time() - start_time)
110 if secs_left <= 0.0:
111 break
112 self._cv.wait(secs_left)
113
114 return self.config
115
117 """
118 UNSTABLE. Return a description of the parameters for the server.
119 Do not use this method as the type that is returned may change.
120
121 @param timeout: time to wait before giving up
122 @type timeout: float
123 """
124 if timeout is None or timeout == 0.0:
125 with self._cv:
126 while self.param_description is None:
127 if rospy.is_shutdown():
128 return None
129 self._cv.wait()
130 else:
131 start_time = time.time()
132 with self._cv:
133 while self.param_description is None:
134 if rospy.is_shutdown():
135 return None
136 secs_left = timeout - (time.time() - start_time)
137 if secs_left <= 0.0:
138 break
139 self._cv.wait(secs_left)
140
141 return self.param_description
142
144 if timeout is None or timeout == 0.0:
145 with self._cv:
146 while self.group_description is None:
147 if rospy.is_shutdown():
148 return None
149 self._cv.wait()
150 else:
151 start_time = time.time()
152 with self._cv:
153 while self.group_description is None:
154 if rospy.is_shutdown():
155 return None
156 secs_left = timeout - (time.time() - start_time)
157 if secs_left <= 0.0:
158 break
159 self._cv.wait(secs_left)
160
161 return self.group_description
162
164 """
165 Change the server's configuration
166
167 @param changes: dictionary of key value pairs for the parameters that are changing
168 @type changes: {str: value}
169 """
170
171 if self.param_description is None:
172 self.get_parameter_descriptions()
173
174
175 if self.param_description is not None:
176 for name, value in changes.items()[:]:
177 if not name is 'groups':
178 dest_type = self._param_types.get(name)
179 if dest_type is None:
180 raise DynamicReconfigureParameterException('don\'t know parameter: %s' % name)
181
182 try:
183 found = False
184 descr = [x for x in self.param_description if x['name'].lower() == name.lower()][0]
185
186
187 if dest_type is bool and type(value) is str:
188 changes[name] = value.lower() in ("yes", "true", "t", "1")
189 found = True
190
191 elif type(value) is str and not descr['edit_method'] == '':
192 enum_descr = eval(descr['edit_method'])
193 found = False
194 for const in enum_descr['enum']:
195 if value.lower() == const['name'].lower():
196 val_type = self._param_type_from_string(const['type'])
197 changes[name] = val_type(const['value'])
198 found = True
199 if not found:
200 changes[name] = dest_type(value)
201
202 except ValueError, e:
203 raise DynamicReconfigureParameterException('can\'t set parameter \'%s\' of %s: %s' % (name, str(dest_type), e))
204
205 if 'groups' in changes.keys():
206 changes['groups'] = self.update_groups(changes['groups'])
207
208 config = encode_config(changes)
209 msg = self._set_service(config).config
210 if self.group_description is None:
211 self.get_group_descriptions()
212 resp = decode_config(msg, self.group_description)
213
214 return resp
215
217 """
218 Changes the servers group configuration
219
220 @param changes: dictionary of key value pairs for the parameters that are changing
221 @type changes: {str: value}
222 """
223
224 descr = self.get_group_descriptions()
225 try:
226 for k,v in changes.items():
227 for p,g in enumerate(descr['groups']):
228 if k == g['name']:
229 g['state'] = v
230 except:
231 groups = []
232 def update_state(group, description):
233 for p,g in enumerate(description['groups']):
234 if g['id'] == group['id']:
235 description['groups'][p]['state'] = group['state']
236 else:
237 update_state(group, g)
238 return description
239
240 for change in changes:
241 descr = update_state(change, descr)
242 return descr
243
245 """
246 Close connections to the server
247 """
248 self._descriptions_sub.unregister()
249 self._updates_sub.unregister()
250
251
252
254 """
255 Retrieve the config_callback
256 """
257 return self._config_callback
258
260 """
261 Set the config_callback
262 """
263 self._config_callback = value
264 if self._config_callback is not None:
265 self._config_callback(self.config)
266
267 config_callback = property(get_config_callback, set_config_callback)
268
269
270
272 """
273 Get the current description_callback
274 """
275 return self._config_callback
276
278 """
279 UNSTABLE. Set the description callback. Do not use as the type of the
280 description callback may change.
281 """
282 self._description_callback = value
283 if self._description_callback is not None:
284 self._description_callback(self.param_description)
285
286 description_callback = property(get_description_callback, set_description_callback)
287
288
289
291 service_name = rospy.resolve_name(self.name + '/' + suffix)
292 if timeout is None or timeout == 0.0:
293 try:
294 rospy.wait_for_service(service_name, 1.0)
295 except rospy.exceptions.ROSException:
296 print >> sys.stderr, 'Waiting for service %s...' % service_name
297 rospy.wait_for_service(service_name, timeout)
298 else:
299 rospy.wait_for_service(service_name, timeout)
300
301 return rospy.ServiceProxy(service_name, ReconfigureSrv)
302
304 topic_name = rospy.resolve_name(self.name + '/' + suffix)
305
306 return rospy.Subscriber(topic_name, type, callback=callback)
307
309 if self.group_description is None:
310 self.get_group_descriptions()
311 self.config = decode_config(msg, self.group_description)
312
313 with self._cv:
314 self._cv.notifyAll()
315 if self._config_callback is not None:
316 self._config_callback(self.config)
317
319 self.group_description = decode_description(msg)
320 self.param_description = extract_params(self.group_description)
321
322
323 self._param_types = {}
324 for p in self.param_description:
325 n, t = p.get('name'), p.get('type')
326 if n is not None and t is not None:
327 self._param_types[n] = self._param_type_from_string(t)
328
329 with self._cv:
330 self._cv.notifyAll()
331 if self._description_callback is not None:
332 self._description_callback(self.param_description)
333
335 if type_str == 'int': return int
336 elif type_str == 'double': return float
337 elif type_str == 'str': return str
338 elif type_str == 'bool': return bool
339 else:
340 raise DynamicReconfigureParameterException('parameter has unknown type: %s. This is a bug in dynamic_reconfigure.' % type_str)
341