interactive_interface.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # License: BSD
00004 #   https://raw.github.com/robotics-in-concert/rocon_multimaster/license/LICENSE
00005 #
00006 
00007 ##############################################################################
00008 # Imports
00009 ##############################################################################
00010 
00011 import re
00012 import copy
00013 import threading
00014 
00015 from gateway_msgs.msg import RemoteRule
00016 
00017 from . import utils
00018 
00019 ##############################################################################
00020 # Classes
00021 ##############################################################################
00022 
00023 
00024 class InteractiveInterface(object):
00025 
00026     '''
00027       Parent interface for flip and pull interfaces.
00028     '''
00029 
00030     def __init__(self, default_rule_blacklist, default_rules, all_targets):
00031         '''
00032           @param default_rule_blacklist : used when in flip/pull all mode
00033           @type dictionary of gateway
00034           @param default_rules : static rules to flip/pull on startup
00035           @type gateway_msgs.msg.RemoteRule[]
00036           @param all_targets : static flip/pull all targets to flip/pull to on startup
00037           @type string[]
00038         '''
00039         # Rules that are active, ie have been flipped or pulled from remote gateways
00040         # keys are connection_types, elements are lists of RemoteRule objects
00041         # This gets aliased to self.flipped or self.pulled as necessary in
00042         # the subclasses
00043         self.active = utils.create_empty_connection_type_dictionary()
00044 
00045         # Default rules used in the xxxAll modes
00046         # dictionary of gateway-gateway_msgs.msg.Rule lists, not RemoteRules!
00047         self._default_blacklist = default_rule_blacklist
00048 
00049         # keys are connection_types, elements are lists of gateway_msgs.msg.RemoteRule objects
00050         # Specific rules used to determine what local rules to flip
00051         self.watchlist = utils.create_empty_connection_type_dictionary()
00052 
00053         # keys are connection_types, elements are lists of utils.Registration objects
00054         # Flips from remote gateways that have been locally registered
00055         self.registrations = utils.create_empty_connection_type_dictionary()
00056 
00057         # Blacklists when doing flip all - different for each gateway, each value
00058         # is one of our usual rule type dictionaries
00059         self._blacklist = {}
00060 
00061         self._lock = threading.Lock()
00062 
00063         # Load up static rules.
00064         for rule in default_rules:
00065             self.add_rule(rule)
00066         for gateway in all_targets:
00067             self.add_all(gateway, [])  # don't add the complexity of extra blacklists yet, maybe later
00068 
00069     ##########################################################################
00070     # Rules
00071     ##########################################################################
00072 
00073     def add_rule(self, remote_rule):
00074         '''
00075           Add a remote rule to the watchlist for monitoring.
00076 
00077           @param remote_rule : the remote rule to add to the watchlist
00078           @type gateway_msgs.msg.RemoteRule
00079 
00080           @return the remote rule, or None if the rule already exists.
00081           @rtype gateway_msgs.msg.RemoteRule || None
00082         '''
00083         result = None
00084         self._lock.acquire()
00085         rule_already_exists = False
00086         # Could be a bit smarter here - given regex expressions an added
00087         # rule may be redundant. It doesn't break the eventual behaviour though
00088         for watched_rule in self.watchlist[remote_rule.rule.type]:
00089             if watched_rule.gateway == remote_rule.gateway and \
00090                watched_rule.rule.name == remote_rule.rule.name and \
00091                watched_rule.rule.node == remote_rule.rule.node:
00092                 rule_already_exists = True
00093                 break
00094         if not rule_already_exists:
00095             self.watchlist[remote_rule.rule.type].append(remote_rule)
00096             result = remote_rule
00097         self._lock.release()
00098         return result
00099 
00100     def remove_rule(self, remote_rule):
00101         '''
00102           Remove a rule. Be a bit careful looking for a rule to remove, depending
00103           on the node name, which can be set (exact rule/node name match) or
00104           None in which case all nodes of that kind of flip will match.
00105 
00106           Handle the remapping appropriately.
00107 
00108           @param remote_rule : the remote rule to remove from the watchlist.
00109           @type gateway_msgs.msg.RemoteRule
00110 
00111           @return Rules remaining in the watchlist
00112           @rtype RemoteRule[]
00113         '''
00114         if remote_rule.rule.node:
00115             # This looks for *exact* matches.
00116             try:
00117                 self._lock.acquire()
00118                 self.watchlist[remote_rule.rule.type].remove(remote_rule)
00119                 self._lock.release()
00120                 return [remote_rule]
00121             except ValueError:
00122                 self._lock.release()
00123                 return []
00124         else:
00125             # This looks for any flip rules which match except for the node name
00126             existing_rules = []
00127             self._lock.acquire()
00128             for existing_rule in self.watchlist[remote_rule.rule.type]:
00129                 if (existing_rule.gateway == remote_rule.gateway) and \
00130                    (existing_rule.rule.name == remote_rule.rule.name):
00131                     existing_rules.append(existing_rule)
00132             for rule in existing_rules:
00133                 self.watchlist[remote_rule.rule.type].remove(rule)  # not terribly optimal
00134             self._lock.release()
00135             return existing_rules
00136 
00137     def add_all(self, gateway, blacklist):
00138         '''
00139           Instead of watching/acting on specific rules, take action
00140           on everything except for rules in a blacklist.
00141 
00142           @param gateway : target remote gateway string id
00143           @type str
00144 
00145           @param blacklist : do not act on rules matching these patterns
00146           @type gateway_msgs.msg.Rule[]
00147 
00148           @return success or failure depending on if it ahs already been set or not
00149           @rtype Bool
00150         '''
00151         self._lock.acquire()
00152         # Blacklist
00153         if gateway in self._blacklist:
00154             self._lock.release()
00155             return False
00156         self._blacklist[gateway] = self._default_blacklist
00157         for rule in blacklist:
00158             self._blacklist[gateway][rule.type].append(rule)
00159         # Flips
00160         for connection_type in utils.connection_types:
00161             remote_rule = RemoteRule()
00162             remote_rule.gateway = gateway
00163             remote_rule.rule.name = '.*'
00164             remote_rule.rule.node = None
00165             remote_rule.rule.type = connection_type
00166             # Remove all other rules for that gateway
00167             self.watchlist[connection_type][:] = [
00168                 rule for rule in self.watchlist[connection_type] if rule.gateway != gateway]
00169             # basically self.add_rule() - do it manually here so we don't deadlock locks
00170             self.watchlist[connection_type].append(remote_rule)
00171         self._lock.release()
00172         return True
00173 
00174     def remove_all(self, gateway):
00175         '''
00176           Remove the add all rule for the specified gateway.
00177 
00178           @param gateway : target remote gateway string id
00179           @type str
00180         '''
00181         self._lock.acquire()
00182         if gateway in self._blacklist:
00183             del self._blacklist[gateway]
00184         for connection_type in utils.connection_types:
00185             for rule in self.watchlist[connection_type]:
00186                 if rule.gateway == gateway:
00187                     # basically self.remove_rule() - do it manually here so we don't deadlock locks
00188                     try:
00189                         self.watchlist[connection_type].remove(rule)
00190                     except ValueError:
00191                         pass  # should never get here
00192         self._lock.release()
00193 
00194     ##########################################################################
00195     # Accessors for Gateway Info
00196     ##########################################################################
00197 
00198     def is_matched(self, rule, rule_name, name, node):
00199         matched = False
00200         name_match_result = re.match(rule_name, name)
00201         if name_match_result and name_match_result.group() == name:
00202             if utils.is_all_pattern(rule_name):
00203                 if self._is_in_blacklist(rule.gateway, rule.rule.type, name, node):
00204                     return False
00205             if rule.rule.node:
00206                 node_match_result = re.match(rule.rule.node, node)
00207                 if node_match_result and node_match_result.group() == node:
00208                     matched = True
00209             else:
00210                 matched = True
00211         return matched
00212 
00213     def getLocalRegistrations(self):
00214         '''
00215           Gets the local registrations for GatewayInfo consumption (flipped ins/pulls).
00216 
00217           We don't need to show the service and node uri's here.
00218 
00219           Basic operation : convert Registration -> RemoteRule for each registration
00220 
00221           @return the list of registrations corresponding to remote interactions
00222           @rtype RemoteRule[]
00223         '''
00224         local_registrations = []
00225         for connection_type in utils.connection_types:
00226             for registration in self.registrations[connection_type]:
00227                 remote_rule = RemoteRule()
00228                 remote_rule.gateway = registration.remote_gateway
00229                 remote_rule.rule.name = registration.connection.rule.name
00230                 remote_rule.rule.node = registration.connection.rule.node
00231                 remote_rule.rule.type = connection_type
00232                 local_registrations.append(remote_rule)
00233         return local_registrations
00234 
00235     def getWatchlist(self):
00236         '''
00237           Gets the watchlist for GatewayInfo consumption.
00238 
00239           @return the list of flip rules that are being watched
00240           @rtype gateway_msgs.msg.RemoteRule[]
00241         '''
00242         watchlist = []
00243         for connection_type in utils.connection_types:
00244             watchlist.extend(copy.deepcopy(self.watchlist[connection_type]))
00245         # ros messages must have string output
00246         for remote in watchlist:
00247             if not remote.rule.node:
00248                 remote.rule.node = 'None'
00249         return watchlist
00250 
00251     ##########################################################################
00252     # Utilities
00253     ##########################################################################
00254 
00255     def find_registration_match(self, remote_gateway, remote_name, remote_node, connection_type):
00256         '''
00257           Check to see if a registration exists. Note that it doesn't use the
00258           local node name in the check. We will get things like unflip requests that
00259           don't have this variable set (that gets autogenerated when registering
00260           the flip), but we need to find the matching registration.
00261 
00262           We then return the registration that matches.
00263 
00264           @param remote_gateway : string remote gateway id
00265           @type string
00266           @param remote_name, remote_node, connection_type : remote connection details
00267           @type string
00268 
00269           @return matching registration or none
00270           @rtype utils.Registration
00271         '''
00272 
00273         matched_registration = None
00274         self._lock.acquire()
00275         for registration in self.registrations[connection_type]:
00276             if (registration.remote_gateway == remote_gateway) and \
00277                (registration.connection.rule.name == remote_name) and \
00278                (registration.connection.rule.node == remote_node) and \
00279                (registration.connection.rule.type == connection_type):
00280                 matched_registration = registration
00281                 break
00282             else:
00283                 continue
00284         self._lock.release()
00285         return matched_registration
00286 
00287     def _is_in_blacklist(self, gateway, connection_type, name, node):
00288         '''
00289           Check if a particular connection is in the blacklist. Use this to
00290           filter connections from the flip_all command.
00291 
00292           @todo move to utils - should be shared with the public interface.
00293         '''
00294         for blacklist_rule in self._blacklist[gateway][connection_type]:
00295             name_match_result = re.match(blacklist_rule.name, name)
00296             if name_match_result and name_match_result.group() == name:
00297                 if blacklist_rule.node:
00298                     node_match_result = re.match(blacklist_rule.node, node)
00299                     if node_match_result and node_match_result.group() == node:
00300                         return True
00301                 else:  # rule.connection.node is None so we don't care about matching the node
00302                     return True
00303         return False


rocon_gateway
Author(s): Daniel Stonier , Jihoon Lee , Piyush Khandelwal
autogenerated on Sat Jun 8 2019 18:48:44