interactive_interface.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # License: BSD
4 # https://raw.github.com/robotics-in-concert/rocon_multimaster/license/LICENSE
5 #
6 
7 ##############################################################################
8 # Imports
9 ##############################################################################
10 
11 import re
12 import copy
13 import threading
14 
15 from gateway_msgs.msg import RemoteRule
16 
17 from . import utils
18 
19 ##############################################################################
20 # Classes
21 ##############################################################################
22 
23 
24 class InteractiveInterface(object):
25 
26  '''
27  Parent interface for flip and pull interfaces.
28  '''
29 
30  def __init__(self, default_rule_blacklist, default_rules, all_targets):
31  '''
32  @param default_rule_blacklist : used when in flip/pull all mode
33  @type dictionary of gateway
34  @param default_rules : static rules to flip/pull on startup
35  @type gateway_msgs.msg.RemoteRule[]
36  @param all_targets : static flip/pull all targets to flip/pull to on startup
37  @type string[]
38  '''
39  # Rules that are active, ie have been flipped or pulled from remote gateways
40  # keys are connection_types, elements are lists of RemoteRule objects
41  # This gets aliased to self.flipped or self.pulled as necessary in
42  # the subclasses
43  self.active = utils.create_empty_connection_type_dictionary()
44 
45  # Default rules used in the xxxAll modes
46  # dictionary of gateway-gateway_msgs.msg.Rule lists, not RemoteRules!
47  self._default_blacklist = default_rule_blacklist
48 
49  # keys are connection_types, elements are lists of gateway_msgs.msg.RemoteRule objects
50  # Specific rules used to determine what local rules to flip
51  self.watchlist = utils.create_empty_connection_type_dictionary()
52 
53  # keys are connection_types, elements are lists of utils.Registration objects
54  # Flips from remote gateways that have been locally registered
55  self.registrations = utils.create_empty_connection_type_dictionary()
56 
57  # Blacklists when doing flip all - different for each gateway, each value
58  # is one of our usual rule type dictionaries
59  self._blacklist = {}
60 
61  self._lock = threading.Lock()
62 
63  # Load up static rules.
64  for rule in default_rules:
65  self.add_rule(rule)
66  for gateway in all_targets:
67  self.add_all(gateway, []) # don't add the complexity of extra blacklists yet, maybe later
68 
69  ##########################################################################
70  # Rules
71  ##########################################################################
72 
73  def add_rule(self, remote_rule):
74  '''
75  Add a remote rule to the watchlist for monitoring.
76 
77  @param remote_rule : the remote rule to add to the watchlist
78  @type gateway_msgs.msg.RemoteRule
79 
80  @return the remote rule, or None if the rule already exists.
81  @rtype gateway_msgs.msg.RemoteRule || None
82  '''
83  result = None
84  self._lock.acquire()
85  rule_already_exists = False
86  # Could be a bit smarter here - given regex expressions an added
87  # rule may be redundant. It doesn't break the eventual behaviour though
88  for watched_rule in self.watchlist[remote_rule.rule.type]:
89  if watched_rule.gateway == remote_rule.gateway and \
90  watched_rule.rule.name == remote_rule.rule.name and \
91  watched_rule.rule.node == remote_rule.rule.node:
92  rule_already_exists = True
93  break
94  if not rule_already_exists:
95  self.watchlist[remote_rule.rule.type].append(remote_rule)
96  result = remote_rule
97  self._lock.release()
98  return result
99 
100  def remove_rule(self, remote_rule):
101  '''
102  Remove a rule. Be a bit careful looking for a rule to remove, depending
103  on the node name, which can be set (exact rule/node name match) or
104  None in which case all nodes of that kind of flip will match.
105 
106  Handle the remapping appropriately.
107 
108  @param remote_rule : the remote rule to remove from the watchlist.
109  @type gateway_msgs.msg.RemoteRule
110 
111  @return Rules remaining in the watchlist
112  @rtype RemoteRule[]
113  '''
114  if remote_rule.rule.node:
115  # This looks for *exact* matches.
116  try:
117  self._lock.acquire()
118  self.watchlist[remote_rule.rule.type].remove(remote_rule)
119  self._lock.release()
120  return [remote_rule]
121  except ValueError:
122  self._lock.release()
123  return []
124  else:
125  # This looks for any flip rules which match except for the node name
126  existing_rules = []
127  self._lock.acquire()
128  for existing_rule in self.watchlist[remote_rule.rule.type]:
129  if (existing_rule.gateway == remote_rule.gateway) and \
130  (existing_rule.rule.name == remote_rule.rule.name):
131  existing_rules.append(existing_rule)
132  for rule in existing_rules:
133  self.watchlist[remote_rule.rule.type].remove(rule) # not terribly optimal
134  self._lock.release()
135  return existing_rules
136 
137  def add_all(self, gateway, blacklist):
138  """
139  Instead of watching/acting on specific rules, take action
140  on everything except for rules in a blacklist.
141 
142  @param gateway : target remote gateway string id
143  @type str
144 
145  @param blacklist : do not act on rules matching these patterns
146  @type gateway_msgs.msg.Rule[]
147 
148  @return success or failure depending on if it ahs already been set or not
149  @rtype Bool
150  """
151  self._lock.acquire()
152  # Blacklist
153  if gateway in self._blacklist:
154  self._lock.release()
155  return False
156  self._blacklist[gateway] = self._default_blacklist
157  for rule in blacklist:
158  self._blacklist[gateway][rule.type].append(rule)
159  # Flips
160  for connection_type in utils.connection_types:
161  remote_rule = RemoteRule()
162  remote_rule.gateway = gateway
163  remote_rule.rule.name = '.*'
164  remote_rule.rule.node = None
165  remote_rule.rule.type = connection_type
166  # Remove all other rules for that gateway
167  self.watchlist[connection_type][:] = [
168  rule for rule in self.watchlist[connection_type] if rule.gateway != gateway]
169  # basically self.add_rule() - do it manually here so we don't deadlock locks
170  self.watchlist[connection_type].append(remote_rule)
171  self._lock.release()
172  return True
173 
174  def remove_all(self, gateway):
175  '''
176  Remove the add all rule for the specified gateway.
177 
178  @param gateway : target remote gateway string id
179  @type str
180  '''
181  self._lock.acquire()
182  if gateway in self._blacklist:
183  del self._blacklist[gateway]
184  for connection_type in utils.connection_types:
185  for rule in self.watchlist[connection_type]:
186  if rule.gateway == gateway:
187  # basically self.remove_rule() - do it manually here so we don't deadlock locks
188  try:
189  self.watchlist[connection_type].remove(rule)
190  except ValueError:
191  pass # should never get here
192  self._lock.release()
193 
194  ##########################################################################
195  # Accessors for Gateway Info
196  ##########################################################################
197 
198  def is_matched(self, rule, rule_name, name, node):
199  matched = False
200  name_match_result = re.match(rule_name, name)
201  if name_match_result and name_match_result.group() == name:
202  if utils.is_all_pattern(rule_name):
203  if self._is_in_blacklist(rule.gateway, rule.rule.type, name, node):
204  return False
205  if rule.rule.node:
206  node_match_result = re.match(rule.rule.node, node)
207  if node_match_result and node_match_result.group() == node:
208  matched = True
209  else:
210  matched = True
211  return matched
212 
214  '''
215  Gets the local registrations for GatewayInfo consumption (flipped ins/pulls).
216 
217  We don't need to show the service and node uri's here.
218 
219  Basic operation : convert Registration -> RemoteRule for each registration
220 
221  @return the list of registrations corresponding to remote interactions
222  @rtype RemoteRule[]
223  '''
224  local_registrations = []
225  for connection_type in utils.connection_types:
226  for registration in self.registrations[connection_type]:
227  remote_rule = RemoteRule()
228  remote_rule.gateway = registration.remote_gateway
229  remote_rule.rule.name = registration.connection.rule.name
230  remote_rule.rule.node = registration.connection.rule.node
231  remote_rule.rule.type = connection_type
232  local_registrations.append(remote_rule)
233  return local_registrations
234 
235  def getWatchlist(self):
236  '''
237  Gets the watchlist for GatewayInfo consumption.
238 
239  @return the list of flip rules that are being watched
240  @rtype gateway_msgs.msg.RemoteRule[]
241  '''
242  watchlist = []
243  for connection_type in utils.connection_types:
244  watchlist.extend(copy.deepcopy(self.watchlist[connection_type]))
245  # ros messages must have string output
246  for remote in watchlist:
247  if not remote.rule.node:
248  remote.rule.node = 'None'
249  return watchlist
250 
251  ##########################################################################
252  # Utilities
253  ##########################################################################
254 
255  def find_registration_match(self, remote_gateway, remote_name, remote_node, connection_type):
256  '''
257  Check to see if a registration exists. Note that it doesn't use the
258  local node name in the check. We will get things like unflip requests that
259  don't have this variable set (that gets autogenerated when registering
260  the flip), but we need to find the matching registration.
261 
262  We then return the registration that matches.
263 
264  @param remote_gateway : string remote gateway id
265  @type string
266  @param remote_name, remote_node, connection_type : remote connection details
267  @type string
268 
269  @return matching registration or none
270  @rtype utils.Registration
271  '''
272 
273  matched_registration = None
274  self._lock.acquire()
275  for registration in self.registrations[connection_type]:
276  if (registration.remote_gateway == remote_gateway) and \
277  (registration.connection.rule.name == remote_name) and \
278  (registration.connection.rule.node == remote_node) and \
279  (registration.connection.rule.type == connection_type):
280  matched_registration = registration
281  break
282  else:
283  continue
284  self._lock.release()
285  return matched_registration
286 
287  def _is_in_blacklist(self, gateway, connection_type, name, node):
288  '''
289  Check if a particular connection is in the blacklist. Use this to
290  filter connections from the flip_all command.
291 
292  @todo move to utils - should be shared with the public interface.
293  '''
294  for blacklist_rule in self._blacklist[gateway][connection_type]:
295  name_match_result = re.match(blacklist_rule.name, name)
296  if name_match_result and name_match_result.group() == name:
297  if blacklist_rule.node:
298  node_match_result = re.match(blacklist_rule.node, node)
299  if node_match_result and node_match_result.group() == node:
300  return True
301  else: # rule.connection.node is None so we don't care about matching the node
302  return True
303  return False
def __init__(self, default_rule_blacklist, default_rules, all_targets)
def is_matched(self, rule, rule_name, name, node)
Accessors for Gateway Info.
def _is_in_blacklist(self, gateway, connection_type, name, node)
def find_registration_match(self, remote_gateway, remote_name, remote_node, connection_type)
Utilities.


rocon_gateway
Author(s): Daniel Stonier , Jihoon Lee , Piyush Khandelwal
autogenerated on Mon Jun 10 2019 14:40:10