Package rocon_gateway :: Module public_interface
[frames] | no frames]

Source Code for Module rocon_gateway.public_interface

  1  #!/usr/bin/env python 
  2  # 
  3  # License: BSD 
  4  #   https://raw.github.com/robotics-in-concert/rocon_multimaster/hydro-devel/rocon_gateway/LICENSE 
  5  # 
  6   
  7  ############################################################################## 
  8  # Imports 
  9  ############################################################################## 
 10   
 11  import copy 
 12  import re 
 13  import threading 
 14   
 15  import rospy 
 16   
 17  # local imports 
 18  import utils 
 19  from gateway_msgs.msg import Rule 
 20   
 21  ############################################################################## 
 22  # Functions 
 23  ############################################################################## 
 24   
 25   
26 -def publicRuleExists(public_rule, public_rules):
27 ''' 28 Checks that the public rule doesn't already exist in the list of public 29 rules (which can represent the public interface or the rules themselves). 30 We only need to compare the name/node as they uniquely identify the 31 name/regex 32 33 @param public_rule : the rule to search for 34 @type Rule 35 36 @param public_rules : list of Rule (public, watchlist or blacklist) 37 @type list : list of Rule objects 38 39 @return True if the public rule exists, False otherwise 40 @rtype bool 41 ''' 42 for rule in public_rules: 43 if rule.name == public_rule.name and \ 44 rule.node == public_rule.node: 45 return True 46 return False
47 48 ############################################################################## 49 # Public Interface 50 ############################################################################## 51 52
53 -class PublicInterface(object):
54 ''' 55 The public interface is the set of rules 56 (pubs/subs/services/actions) that are exposed and made available for 57 freely sharing with a multimaster system. 58 59 It consists of: 60 * list of currently available rules to be shared 61 * list of rules and filters that will be watched 62 and shared if they become available 63 '''
64 - def __init__(self, default_rule_blacklist, default_rules):
65 ''' 66 Initialises the public interface 67 68 @param default_rule_blacklist : connection type keyed dictionary of rules 69 @type str keyed dictionary of gateway_msgs.msg.Rule[] 70 71 @param default_rules : connection type keyed dictionary of rules 72 @type str keyed dictionary of gateway_msgs.msg.Rule[] 73 ''' 74 # List of rules to be monitored and (un)advertised as they 75 # become (un)available 76 self.watchlist = utils.create_empty_connection_type_dictionary() 77 78 # Default rules that cannot be advertised - used in AdvertiseAll mode 79 self._default_blacklist = default_rule_blacklist 80 81 # Default + custom blacklist - used in AdvertiseAll mode 82 self.blacklist = self._default_blacklist 83 84 # list of fully qualified connections currently being advertised 85 self.public = utils.create_empty_connection_type_dictionary() 86 87 self.advertise_all_enabled = False 88 89 self.lock = threading.Lock() 90 91 # Load up static rules. 92 for connection_type in utils.connection_types: 93 for rule in default_rules[connection_type]: 94 self.add_rule(rule)
95 96 ########################################################################## 97 # Public Interfaces 98 ########################################################################## 99
100 - def add_rule(self, rule):
101 ''' 102 Watch for a new public rule, as described for by the incoming message. 103 104 @param rule : a rule msg from the advertise call 105 @type Rule 106 107 @return the rule if added, or None if the rule exists already 108 @rtype Rule || None 109 ''' 110 result = None 111 self.lock.acquire() 112 if not publicRuleExists(rule, self.watchlist[rule.type]): 113 self.watchlist[rule.type].append(rule) 114 result = rule 115 self.lock.release() 116 rospy.loginfo("Gateway : adding rule to public watchlist %s" % utils.format_rule(rule)) 117 return result
118
119 - def remove_rule(self, rule):
120 ''' 121 Attempt to remove a watchlist rule from the public interface. Be a 122 bit careful looking for a rule to remove, depending on the node name, 123 which can be set (exact rule/node name match) or None in which case all 124 nodes of that kind of advertisement will match. 125 126 @param rule : a rule to unadvertise 127 @type Rule 128 129 @return the list of rules removed 130 @rtype Rule[] 131 ''' 132 133 rospy.loginfo("Gateway : (req) unadvertise %s" % utils.format_rule(rule)) 134 135 if rule.node: 136 # This looks for *exact* matches. 137 try: 138 self.lock.acquire() 139 self.watchlist[rule.type].remove(rule) 140 self.lock.release() 141 return [rule] 142 except ValueError: 143 self.lock.release() 144 return [] 145 else: 146 # This looks for any flip rules which match except for the node name 147 # also no need to check for type with the dic keys like they are 148 existing_rules = [] 149 self.lock.acquire() 150 for existing_rule in self.watchlist[rule.type]: 151 if (existing_rule.name == rule.name): 152 existing_rules.append(existing_rule) 153 for rule in existing_rules: 154 self.watchlist[rule.type].remove(existing_rule) # not terribly optimal 155 self.lock.release() 156 return existing_rules
157
158 - def advertise_all(self, blacklist):
159 ''' 160 Allow all rules apart from the ones in the provided blacklist + 161 default blacklist 162 163 @param blacklist : list of Rule objects 164 @type list : list of Rule objects 165 166 @return failure if already advertising all, success otherwise 167 @rtype bool 168 ''' 169 rospy.loginfo("Gateway : received a request advertise everything!") 170 self.lock.acquire() 171 172 # Check if advertise all already enabled 173 if self.advertise_all_enabled: 174 self.lock.release() 175 return False 176 self.advertise_all_enabled = True 177 178 # generate watchlist 179 self.watchlist = utils.create_empty_connection_type_dictionary() # easy hack for getting a clean watchlist 180 for connection_type in utils.connection_types: 181 allow_all_rule = Rule() 182 allow_all_rule.name = '.*' 183 allow_all_rule.type = connection_type 184 allow_all_rule.node = '.*' 185 self.watchlist[connection_type].append(allow_all_rule) 186 187 # generate blacklist (while making sure only unique rules get added) 188 self.blacklist = copy.deepcopy(self._default_blacklist) 189 for rule in blacklist: 190 if not publicRuleExists(rule, self.blacklist[rule.type]): 191 self.blacklist[rule.type].append(rule) 192 193 self.lock.release() 194 return True
195
196 - def unadvertise_all(self):
197 ''' 198 Disallow the allow all mode, if enabled. If allow all mode is not 199 enabled, remove everything from the public interface 200 ''' 201 rospy.loginfo("Gateway : received a request to remove all advertisements!") 202 self.lock.acquire() 203 204 # stop advertising all 205 self.advertise_all_enabled = False 206 207 # easy hack for resetting the watchlist and blacklist 208 self.watchlist = utils.create_empty_connection_type_dictionary() 209 self.blacklist = self._default_blacklist 210 211 self.lock.release()
212 213 ########################################################################## 214 # List Accessors 215 ########################################################################## 216
217 - def getInterface(self):
218 l = [] 219 self.lock.acquire() 220 for connection_type in utils.connection_types: 221 l.extend([connection.rule for connection in self.public[connection_type]]) 222 self.lock.release() 223 return l
224
225 - def getWatchlist(self):
226 l = [] 227 self.lock.acquire() 228 for connection_type in utils.connection_types: 229 l.extend(self.watchlist[connection_type]) 230 self.lock.release() 231 return l
232
233 - def getBlacklist(self):
234 l = [] 235 self.lock.acquire() 236 for connection_type in utils.connection_types: 237 l.extend(self.blacklist[connection_type]) 238 self.lock.release() 239 return l
240 241 ########################################################################## 242 # Filter 243 ########################################################################## 244
245 - def _matchAgainstRuleList(self, rules, rule):
246 ''' 247 Match a given rule/rule against a given rule list 248 249 @param rules : the rules against which to match 250 @type dict of list of Rule objects 251 @param rule : the given rule/rule to match 252 @type Rule 253 @return the list of rules matched, None if no rules found 254 @rtype list of Rules || None 255 ''' 256 matched = False 257 for r in rules[rule.type]: 258 name_match_result = re.match(r.name, rule.name) 259 if name_match_result and name_match_result.group() == rule.name: 260 if r.node: 261 node_match_result = re.match(r.node, rule.node) 262 if node_match_result and node_match_result.group() == rule.node: 263 matched = True 264 else: 265 matched = True 266 if matched: 267 break 268 return matched
269
270 - def _allowRule(self, rule):
271 ''' 272 Determines whether a given rule should be allowed given the 273 status of the current watchlist and blacklist 274 275 @param rule : the given rule/rule to match 276 @type Rule 277 @return whether rule is allowed 278 @rtype bool 279 ''' 280 self.lock.acquire() 281 matched_rules = self._matchAgainstRuleList(self.watchlist, rule) 282 matched_blacklisted_rules = self._matchAgainstRuleList(self.blacklist, rule) 283 self.lock.release() 284 success = False 285 if matched_rules and not matched_blacklisted_rules: 286 success = True 287 return success
288
289 - def _generatePublic(self, rule):
290 ''' 291 Given a rule, determines if the rule is allowed. If it is 292 allowed, then returns the corresponding Rule object 293 294 @param rules : the given rules to match 295 @type Rule 296 @return The generated Rule if allowed, None if no match 297 @rtype Rule || None 298 ''' 299 if self._allowRule(rule): 300 return Rule(rule) 301 return None
302
303 - def update(self, connections):
304 ''' 305 Checks a list of rules and determines which ones should be 306 added/removed to the public interface. Modifies the public interface 307 accordingly, and returns the list of rules to the gateway for 308 hub operations 309 310 @param rules: the list of rules available locally 311 @type dict of lists of Rule objects 312 313 @return: new public connections, as well as connections to be removed 314 @rtype: Connection[], Connection[] 315 ''' 316 # SLOW, EASY METHOD 317 public = utils.create_empty_connection_type_dictionary() 318 new_public = utils.create_empty_connection_type_dictionary() 319 removed_public = utils.create_empty_connection_type_dictionary() 320 diff = lambda l1,l2: [x for x in l1 if x not in l2] # diff of lists 321 for connection_type in connections: 322 for connection in connections[connection_type]: 323 if self._allowRule(connection.rule): 324 public[connection_type].append(connection) 325 new_public[connection_type] = diff(public[connection_type],self.public[connection_type]) 326 removed_public[connection_type] = diff(self.public[connection_type],public[connection_type]) 327 328 self.lock.acquire() 329 self.public = public 330 self.lock.release() 331 332 return new_public, removed_public
333