3131# POSSIBILITY OF SUCH DAMAGE.
3232
3333import fnmatch
34- import rospy
35- import threading
36-
3734from json import loads , dumps
35+ import threading
3836
37+ from rcl_interfaces .msg import Parameter , ParameterType , ParameterValue
38+ from rcl_interfaces .srv import ListParameters
39+ import rclpy
40+ from ros2node .api import get_absolute_node_name
41+ from ros2param .api import call_get_parameters , call_set_parameters , get_parameter_value
3942
4043""" Methods to interact with the param server. Values have to be passed
4144as JSON in order to facilitate dynamically typed SRV messages """
4245
43- # rospy parameter server isn't thread-safe
46+ # Ensure thread safety for setting / getting parameters.
4447param_server_lock = threading .RLock ()
48+ _node = None
49+
50+ _parameter_type_mapping = ['' , 'bool_value' , 'integer_value' , 'double_value' , 'string_value' , 'byte_array_value'
51+ 'bool_array_value' , 'integer_array_value' , 'double_array_value' , 'string_array_value' ]
52+
53+
54+ def init ():
55+ """
56+ Initializes params module with a rclpy.node.Node for further use.
57+ This function has to be called before any other for the module to work.
58+ """
59+ global _node
60+ # TODO(@jubeira): remove this node; use rosapi node with MultiThreadedExecutor or
61+ # async / await to prevent the service calls from blocking.
62+ _node = rclpy .create_node ('rosapi_params' )
63+
64+
65+ def set_param (node_name , name , value , params_glob ):
66+ """ Sets a parameter in a given node """
4567
46- def set_param (name , value , params_glob ):
4768 if params_glob and not any (fnmatch .fnmatch (str (name ), glob ) for glob in params_glob ):
4869 # If the glob list is not empty and there are no glob matches,
4970 # stop the attempt to set the parameter.
@@ -55,48 +76,139 @@ def set_param(name, value, params_glob):
5576 d = loads (value )
5677 except ValueError :
5778 raise Exception ("Due to the type flexibility of the ROS parameter server, the value argument to set_param must be a JSON-formatted string." )
79+
80+ node_name = get_absolute_node_name (node_name )
5881 with param_server_lock :
59- rospy .set_param (name , d )
60-
61-
62- def get_param (name , default , params_glob ):
82+ _set_param (node_name , name , value )
83+
84+
85+ def _set_param (node_name , name , value , parameter_type = None ):
86+ """
87+ Internal helper function for set_param.
88+ Attempts to set the given parameter in the target node with the desired value,
89+ deducing the parameter type if it's not specified.
90+ parameter_type allows forcing a type for the given value; this is useful to delete parameters.
91+ """
92+ parameter = Parameter ()
93+ parameter .name = name
94+ if parameter_type is None :
95+ parameter .value = get_parameter_value (string_value = value )
96+ else :
97+ parameter .value = ParameterValue ()
98+ parameter .value .type = parameter_type
99+ if parameter_type != ParameterType .PARAMETER_NOT_SET :
100+ setattr (parameter .value , _parameter_type_mapping [parameter_type ])
101+
102+ try :
103+ # call_get_parameters will fail if node does not exist.
104+ call_set_parameters (node = _node , node_name = node_name , parameters = [parameter ])
105+ except :
106+ pass
107+
108+
109+ def get_param (node_name , name , default , params_glob ):
110+ """ Gets a parameter from a given node """
111+
63112 if params_glob and not any (fnmatch .fnmatch (str (name ), glob ) for glob in params_glob ):
64113 # If the glob list is not empty and there are no glob matches,
65114 # stop the attempt to get the parameter.
66115 return
67116 # If the glob list is empty (i.e. false) or the parameter matches
68117 # one of the glob strings, continue to get the parameter.
69- d = None
70118 if default is not "" :
71119 try :
72- d = loads (default )
120+ default = loads (default )
73121 except ValueError :
74- d = default
122+ pass # Keep default without modifications.
123+
124+ node_name = get_absolute_node_name (node_name )
75125 with param_server_lock :
76- value = rospy .get_param (name , d )
126+ try :
127+ # call_get_parameters will fail if node does not exist.
128+ response = call_get_parameters (
129+ node = _node , node_name = node_name ,
130+ parameter_names = [name ])
131+ pvalue = response .values [0 ]
132+ # if type is 0 (parameter not set), the next line will raise an exception
133+ # and return value shall go to default.
134+ value = getattr (pvalue , _parameter_type_mapping [pvalue .type ])
135+ except :
136+ # If either the node or the parameter does not exist, return default.
137+ value = default
138+
77139 return dumps (value )
78140
79- def has_param (name , params_glob ):
141+
142+ def has_param (node_name , name , params_glob ):
143+ """ Checks whether a given node has a parameter or not """
144+
80145 if params_glob and not any (fnmatch .fnmatch (str (name ), glob ) for glob in params_glob ):
81146 # If the glob list is not empty and there are no glob matches,
82147 # stop the attempt to set the parameter.
83148 return False
84149 # If the glob list is empty (i.e. false) or the parameter matches
85150 # one of the glob strings, check whether the parameter exists.
151+ node_name = get_absolute_node_name (node_name )
86152 with param_server_lock :
87- return rospy .has_param (name )
153+ try :
154+ response = call_get_parameters (
155+ node = _node , node_name = node_name ,
156+ parameter_names = [name ])
157+ except :
158+ return False
159+
160+ return response .values [0 ].type > 0 and response .values [0 ].type < len (_parameter_type_mapping )
161+
162+
163+ def delete_param (node_name , name , params_glob ):
164+ """ Deletes a parameter in a given node """
88165
89- def delete_param (name , params_glob ):
90166 if params_glob and not any (fnmatch .fnmatch (str (name ), glob ) for glob in params_glob ):
91167 # If the glob list is not empty and there are no glob matches,
92168 # stop the attempt to delete the parameter.
93169 return
94170 # If the glob list is empty (i.e. false) or the parameter matches
95171 # one of the glob strings, continue to delete the parameter.
96- if has_param (name , params_glob ):
172+ node_name = get_absolute_node_name (node_name )
173+ if has_param (node_name , name , params_glob ):
97174 with param_server_lock :
98- rospy .delete_param (name )
175+ _set_param (node_name , name , None , ParameterType .PARAMETER_NOT_SET )
176+
177+
178+ def get_param_names (node_name , params_glob ):
179+ """ Gets list of parameter names for a given node """
180+
181+ node_name = get_absolute_node_name (node_name )
182+
183+ with param_server_lock :
184+ if params_glob :
185+ # If there is a parameter glob, filter by it.
186+ return list (filter (
187+ lambda x : any (fnmatch .fnmatch (str (x ), glob ) for glob in params_glob ),
188+ _get_param_names (node_name )))
189+ else :
190+ # If there is no parameter glob, don't filter.
191+ return _get_param_names (node_name )
192+
99193
194+ def _get_param_names (node_name ):
195+ client = _node .create_client (ListParameters , '{}/list_parameters' .format (node_name ))
196+
197+ ready = client .wait_for_service (timeout_sec = 5.0 )
198+ if not ready :
199+ raise RuntimeError ('Wait for list_parameters service timed out' )
200+
201+ request = ListParameters .Request ()
202+ future = client .call_async (request )
203+ rclpy .spin_until_future_complete (_node , future )
204+ response = future .result ()
205+ if response is not None :
206+ return response .result .names
207+ else :
208+ return []
209+
210+
211+ # TODO(@jubeira): functions to be ported below.
100212def search_param (name , params_glob ):
101213 if params_glob and not any (fnmatch .fnmatch (str (name ), glob ) for glob in params_glob ):
102214 # If the glob list is not empty and there are no glob matches,
@@ -105,13 +217,3 @@ def search_param(name, params_glob):
105217 # If the glob list is empty (i.e. false) or the parameter matches
106218 # one of the glob strings, continue to find the parameter.
107219 return rospy .search_param (name )
108-
109- def get_param_names (params_glob ):
110- with param_server_lock :
111- if params_glob :
112- # If there is a parameter glob, filter by it.
113- return filter (lambda x : any (fnmatch .fnmatch (str (x ), glob ) for glob in params_glob ), rospy .get_param_names ())
114- else :
115- # If there is no parameter glob, don't filter.
116- return rospy .get_param_names ()
117-
0 commit comments