conductor_graph_info.py
Go to the documentation of this file.
00001 #
00002 # License: BSD
00003 #   https://raw.github.com/robotics-in-concert/rocon_qt_gui/license/LICENSE
00004 #
00005 ##############################################################################
00006 # Imports
00007 ##############################################################################
00008 
00009 import threading
00010 
00011 import rospy
00012 import concert_msgs.msg as concert_msgs
00013 import rocon_python_comms
00014 import rocon_console.console as console
00015 from .concert_client import ConcertClient
00016 
00017 ##############################################################################
00018 # Graph
00019 ##############################################################################
00020 
00021 
00022 class ConductorGraphInfo(object):
00023     def __init__(self, change_callback, periodic_callback):
00024         '''
00025         Creates the polling topics necessary for updating statistics
00026         about the running gateway-hub network.
00027         '''
00028         self._last_update = 0
00029         self.concert_clients = {}  # dictionary of .concert_client.ConcertClient objects keyed by concert alias
00030         self.is_conductor = False
00031         self.namespace = None
00032         self._trigger_shutdown = False
00033 
00034         #Rubbish to clear out once rocon_gateway_graph is integrated
00035         self._change_callback = change_callback
00036         self._periodic_callback = periodic_callback
00037         self._thread = threading.Thread(target=self._setup_subscribers)
00038         self._thread.start()
00039 
00040     def shutdown(self):
00041         self._trigger_shutdown = True
00042         self._thread.join()
00043 
00044     def _setup_subscribers(self):
00045         """
00046         Hunt for the conductor's namespace and setup subscribers.
00047         """
00048         while not rospy.is_shutdown() and not self._trigger_shutdown:
00049             try:
00050                 graph_topic_name = rocon_python_comms.find_topic('concert_msgs/ConductorGraph', timeout=rospy.rostime.Duration(0.1), unique=True)
00051                 (namespace, unused_topic_name) = graph_topic_name.rsplit('/', 1)
00052                 clients_topic_name = namespace + "/concert_clients"  # this is assuming they didn't remap this bugger.
00053                 break
00054             except rocon_python_comms.NotFoundException:
00055                 pass  # just loop around
00056         if rospy.is_shutdown() or self._trigger_shutdown:
00057             return
00058         self.is_conductor = True
00059         self.namespace = namespace
00060 
00061         print(console.yellow + "Found the conductor, setting up subscribers inside %s" % namespace + console.reset)
00062         # get data on all clients, even those not connected
00063         rospy.Subscriber(graph_topic_name, concert_msgs.ConductorGraph, self._update_clients_callback)
00064         # get the periodic data of connected clients
00065         rospy.Subscriber(clients_topic_name, concert_msgs.ConcertClients, self.update_connection_statistics)
00066 
00067     def _update_clients_callback(self, msg):
00068         '''
00069         Update the cached list of concert clients when a client comes, goes or
00070         changes its state. This update happens rather infrequently with every
00071         message supplied by the conductor's latched graph publisher.
00072         '''
00073         #print("[conductor_graph_info] : update clients callback")
00074         self._graph = msg
00075         # sneaky way of getting all the states and the lists
00076         visible_concert_clients_by_name = []
00077         for state in msg.__slots__:
00078             if state == concert_msgs.ConcertClientState.GONE:
00079                 continue
00080             concert_clients = getattr(msg, state)  # by state
00081             for concert_client in concert_clients:  # concert_msgs.ConcertClient
00082                 visible_concert_clients_by_name.append(concert_client.name)
00083                 if concert_client.name in self.concert_clients.keys():
00084                     self.concert_clients[concert_client.name].is_new = False
00085                     self.concert_clients[concert_client.name].update(concert_client)
00086                 else:
00087                     self.concert_clients[concert_client.name] = ConcertClient(concert_client)  # create extended ConcertClient class from msg
00088         # remove any that are no longer visible
00089         lost_clients_by_name = [concert_alias for concert_alias in self.concert_clients.keys() if concert_alias not in visible_concert_clients_by_name]
00090         for concert_alias in lost_clients_by_name:
00091             del self.concert_clients[concert_alias]
00092         self._change_callback()
00093 
00094     def update_connection_statistics(self, msg):
00095         '''
00096         Update the current list of concert clients' connection statistics. This
00097         happens periodically with every message supplied by the conductor's periodic
00098         publisher.
00099 
00100         :param msg concert_msgs.ConcertClients : graph of concert connected/connectable clients.
00101         '''
00102         #print("[conductor_graph_info]: update_connection_statistics")
00103         for state in msg.__slots__:
00104             concert_clients = getattr(msg, state)  # by state
00105             for concert_client in concert_clients:  # concert_msgs.ConcertClient
00106                 if concert_client.name in self.concert_clients.keys():
00107                     # pick up the changing information in the msg and pass it to our class
00108                     self.concert_clients[concert_client.name].update(concert_client)
00109                 else:
00110                     pass  # just ignore it, the changes topic will pick up and drop new/old clients
00111         self._change_callback()
00112         self._periodic_callback()
00113 
00114 #     def _compare_client_info_list(self):
00115 #         """
00116 #           Not currently using this, but would be a more optimal way of indentifying when the graph
00117 #           changes so we can tell the drawing functions when to update rather than at every step.
00118 #
00119 #           You would insert this logic into the update_connection_statistics to flag when you would
00120 #           want a change and/or a period callback.
00121 #         """
00122 #         result = True
00123 #         pre = self._pre_client_info_list
00124 #         cur = self._client_info_list
00125 #         for k in cur.values():
00126 #             client_name = k.msg.name
00127 #             if not client_name in pre.keys():
00128 #                 continue
00129 #             if pre[client_name].msg.state != cur[client_name].msg.state:
00130 #                 result = False
00131 #             elif pre[client_name].get_connection_strength() != cur[client_name].get_connection_strength():
00132 #                 result = False
00133 #             elif pre[client_name].msg.conn_stats.gateway_available != cur[client_name].msg.conn_stats.gateway_available:
00134 #                 result = False
00135 #
00136 #         return result


concert_utilities
Author(s): Daniel Stonier
autogenerated on Thu Jun 6 2019 18:17:52