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 from threading import RLock
34
35 from rosgraph.names import ns_join, GLOBALNS, SEP, is_global, is_private, canonicalize_name
36
38 """
39 helper recursive routine for getParamNames()
40 @param names: list of param names to append to
41 @type names: [str]
42 @param d: parameter tree node
43 @type d: dict
44 @param key: parameter key for tree node d
45 @type key: str
46 """
47
48
49 for k,v in d.items():
50 if type(v) == dict:
51 _get_param_names(names, ns_join(key, k), v)
52 else:
53 names.append(ns_join(key, k))
54
56
58 """
59 ctor.
60 @param subscribers: parameter subscribers
61 @type subscribers: Registrations
62 """
63 self.lock = RLock()
64 self.parameters = {}
65 self.reg_manager = reg_manager
66
68 """
69 Get list of all parameter names stored on this server.
70
71 @return: [code, statusMessage, parameterNameList]
72 @rtype: [int, str, [str]]
73 """
74 try:
75 self.lock.acquire()
76 param_names = []
77 _get_param_names(param_names, '/', self.parameters)
78 finally:
79 self.lock.release()
80 return param_names
81
83 """
84 Search for matching parameter key for search param
85 key. Search for key starts at ns and proceeds upwards to
86 the root. As such, search_param should only be called with a
87 relative parameter name.
88
89 search_param's behavior is to search for the first partial match.
90 For example, imagine that there are two 'robot_description' parameters:
91
92 - /robot_description
93 - /robot_description/arm
94 - /robot_description/base
95
96 - /pr2/robot_description
97 - /pr2/robot_description/base
98
99 If I start in the namespace /pr2/foo and search for
100 'robot_description', search_param will match
101 /pr2/robot_description. If I search for 'robot_description/arm'
102 it will return /pr2/robot_description/arm, even though that
103 parameter does not exist (yet).
104
105 @param ns: namespace to begin search from.
106 @type ns: str
107 @param key: Parameter key.
108 @type key: str
109 @return: key of matching parameter or None if no matching
110 parameter.
111 @rtype: str
112 """
113 if not key or is_private(key):
114 raise ValueError("invalid key")
115 if not is_global(ns):
116 raise ValueError("namespace must be global")
117 if is_global(key):
118 if self.has_param(key):
119 return key
120 else:
121 return None
122
123
124
125
126
127 key_namespaces = [x for x in key.split(SEP) if x]
128 key_ns = key_namespaces[0]
129
130
131
132 search_key = ns_join(ns, key_ns)
133 if self.has_param(search_key):
134
135 return ns_join(ns, key)
136
137 namespaces = [x for x in ns.split(SEP) if x]
138 for i in range(1, len(namespaces)+1):
139 search_key = SEP + SEP.join(namespaces[0:-i] + [key_ns])
140 if self.has_param(search_key):
141
142
143 full_key = SEP + SEP.join(namespaces[0:-i] + [key])
144 return full_key
145 return None
146
148 """
149 Get the parameter in the parameter dictionary.
150
151 @param key: parameter key
152 @type key: str
153 @return: parameter value
154 """
155 try:
156 self.lock.acquire()
157 val = self.parameters
158 if key != GLOBALNS:
159
160 namespaces = [x for x in key.split(SEP)[1:] if x]
161 for ns in namespaces:
162 if not type(val) == dict:
163 raise KeyError(val)
164 val = val[ns]
165 return val
166 finally:
167 self.lock.release()
168
169 - def set_param(self, key, value, notify_task=None, caller_id=None):
170 """
171 Set the parameter in the parameter dictionary.
172
173 @param key: parameter key
174 @type key: str
175 @param value: parameter value
176 @param notify_task: function to call with
177 subscriber updates. updates is of the form
178 [(subscribers, param_key, param_value)*]. The empty dictionary
179 represents an unset parameter.
180 @type notify_task: fn(updates)
181 @param caller_id: the caller id
182 @type caller_id: str
183 """
184 try:
185 self.lock.acquire()
186 if key == GLOBALNS:
187 if type(value) != dict:
188 raise TypeError("cannot set root of parameter tree to non-dictionary")
189 self.parameters = value
190 else:
191 namespaces = [x for x in key.split(SEP) if x]
192
193 value_key = namespaces[-1]
194 namespaces = namespaces[:-1]
195 d = self.parameters
196
197 for ns in namespaces:
198 if not ns in d:
199 new_d = {}
200 d[ns] = new_d
201 d = new_d
202 else:
203 val = d[ns]
204
205 if type(val) != dict:
206 d[ns] = val = {}
207 d = val
208
209 d[value_key] = value
210
211
212 if notify_task:
213 updates = compute_param_updates(self.reg_manager.param_subscribers, key, value, caller_id)
214 if updates:
215 notify_task(updates)
216 finally:
217 self.lock.release()
218
219
221 """
222 @param key: parameter key
223 @type key: str
224 @param registration_args: additional args to pass to
225 subscribers.register. First parameter is always the parameter
226 key.
227 @type registration_args: tuple
228 """
229 if key != SEP:
230 key = canonicalize_name(key) + SEP
231 try:
232 self.lock.acquire()
233
234 try:
235 val = self.get_param(key)
236 except KeyError:
237
238 val = {}
239 self.reg_manager.register_param_subscriber(key, *registration_args)
240 return val
241 finally:
242 self.lock.release()
243
244
246 """
247 @param key str: parameter key
248 @type key: str
249 @param unregistration_args: additional args to pass to
250 subscribers.unregister. i.e. unregister will be called with
251 (key, *unregistration_args)
252 @type unregistration_args: tuple
253 @return: return value of subscribers.unregister()
254 """
255 if key != SEP:
256 key = canonicalize_name(key) + SEP
257 return self.reg_manager.unregister_param_subscriber(key, *unregistration_args)
258
260 """
261 Delete the parameter in the parameter dictionary.
262 @param key str: parameter key
263 @param notify_task fn(updates): function to call with
264 subscriber updates. updates is of the form
265 [(subscribers, param_key, param_value)*]. The empty dictionary
266 represents an unset parameter.
267 """
268 try:
269 self.lock.acquire()
270 if key == GLOBALNS:
271 raise KeyError("cannot delete root of parameter tree")
272 else:
273
274 namespaces = [x for x in key.split(SEP) if x]
275
276 value_key = namespaces[-1]
277 namespaces = namespaces[:-1]
278 d = self.parameters
279
280 for ns in namespaces:
281 if type(d) != dict or not ns in d:
282 raise KeyError(key)
283 else:
284 d = d[ns]
285
286 if not value_key in d:
287 raise KeyError(key)
288 else:
289 del d[value_key]
290
291
292 if notify_task:
293 updates = compute_param_updates(self.reg_manager.param_subscribers, key, {})
294 if updates:
295 notify_task(updates)
296 finally:
297 self.lock.release()
298
300 """
301 Test for parameter existence
302
303 @param key: parameter key
304 @type key: str
305 @return: True if parameter set, False otherwise
306 @rtype: bool
307 """
308 try:
309
310
311 self.get_param(key)
312 return True
313 except KeyError:
314 return False
315
317 """
318 Compute which subscribers should be notified based on the parameter update
319 @param param_key: key of updated parameter
320 @type param_key: str
321 @param param_value: value of updated parameter
322 @param all_keys: (internal use only) list of parameter keys
323 to append to for recursive calls.
324 @type all_keys: [str]
325 @return: list of parameter keys. All keys will be canonicalized with trailing slash.
326 @rtype: [str]
327 """
328 if all_keys is None:
329 all_keys = []
330 for k, v in param_value.items():
331 new_k = ns_join(param_key, k) + SEP
332 all_keys.append(new_k)
333 if type(v) == dict:
334 _compute_all_keys(new_k, v, all_keys)
335 return all_keys
336
338 """
339 Compute subscribers that should be notified based on the parameter update
340 @param subscribers: parameter subscribers
341 @type subscribers: Registrations
342 @param param_key: parameter key
343 @type param_key: str
344 @param param_value: parameter value
345 @type param_value: str
346 @param caller_id_to_ignore: the caller to ignore
347 @type caller_id_to_ignore: str
348 """
349
350
351
352 if not subscribers:
353 return []
354
355
356
357 if param_key != SEP:
358 param_key = canonicalize_name(param_key) + SEP
359
360
361 if type(param_value) == dict:
362 all_keys = _compute_all_keys(param_key, param_value)
363 else:
364 all_keys = None
365
366 updates = []
367
368
369 for sub_key in subscribers.iterkeys():
370 ns_key = sub_key
371 if ns_key[-1] != SEP:
372 ns_key = sub_key + SEP
373 if param_key.startswith(ns_key):
374 node_apis = subscribers[sub_key]
375 if caller_id_to_ignore is not None:
376 node_apis = [
377 (caller_id, caller_api)
378 for (caller_id, caller_api) in node_apis
379 if caller_id != caller_id_to_ignore]
380 updates.append((node_apis, param_key, param_value))
381 elif all_keys is not None and ns_key.startswith(param_key) \
382 and not sub_key in all_keys:
383
384 node_apis = subscribers[sub_key]
385 updates.append((node_apis, sub_key, {}))
386
387
388 if all_keys is not None:
389
390 for key in all_keys:
391 if key in subscribers:
392
393 sub_key = key[len(param_key):]
394 namespaces = [x for x in sub_key.split(SEP) if x]
395 val = param_value
396 for ns in namespaces:
397 val = val[ns]
398
399 updates.append((subscribers[key], key, val))
400
401 return updates
402