advertise_service.py
Go to the documentation of this file.
00001 from rosbridge_library.internal.ros_loader import get_service_class
00002 from rosbridge_library.internal import message_conversion
00003 
00004 from rosbridge_library.capability import Capability
00005 import rospy
00006 from datetime import datetime
00007 import time
00008 import threading
00009 
00010 
00011 
00012 class ServiceList():
00013     """
00014     Singleton class to hold lists of registered services in one 'global' object
00015     """
00016     class __impl:
00017         """ Implementation of the singleton interface """
00018         def spam(self):
00019             """ Test method, return singleton id """
00020             return id(self)
00021 
00022     __instance = None
00023     list = {}
00024 
00025     def __init__(self):
00026         """ Create singleton instance """
00027         if ServiceList.__instance is None:
00028             ServiceList.__instance = ServiceList.__impl()
00029             self.list = {}
00030 
00031         self.__dict__['_ServiceList__instance'] = ServiceList.__instance
00032 
00033     def __getattr__(self, attr):
00034         """ Delegate access to implementation """
00035         return getattr(self.__instance, attr)
00036 
00037     def __setattr__(self, attr, value):
00038         """ Delegate access to implementation """
00039         return setattr(self.__instance, attr, value)
00040 
00041 class RequestList():
00042     """
00043     Singleton class to hold lists of received requests in one 'global' object
00044     """
00045     class __impl:
00046         """ Implementation of the singleton interface """
00047         def spam(self):
00048             """ Test method, return singleton id """
00049             return id(self)
00050 
00051     __instance = None
00052     list = {}
00053 
00054     def __init__(self):
00055         """ Create singleton instance """
00056         if RequestList.__instance is None:
00057             RequestList.__instance = RequestList.__impl()
00058             self.list = {}
00059 
00060         self.__dict__['_RequestList__instance'] = RequestList.__instance
00061 
00062     def __getattr__(self, attr):
00063         """ Delegate access to implementation """
00064         return getattr(self.__instance, attr)
00065 
00066     def __setattr__(self, attr, value):
00067         """ Delegate access to implementation """
00068         return setattr(self.__instance, attr, value)
00069 
00070 
00071 class ReceivedResponses():
00072     """
00073     Singleton class to hold lists of received service responses in one 'global' object
00074     """
00075     class __impl:
00076         """ Implementation of the singleton interface """
00077         def spam(self):
00078             """ Test method, return singleton id """
00079             return id(self)
00080 
00081     __instance = None
00082     list = {}
00083 
00084     def __init__(self):
00085         """ Create singleton instance """
00086         if ReceivedResponses.__instance is None:
00087             ReceivedResponses.__instance = ReceivedResponses.__impl()
00088             self.list = {}
00089 
00090         self.__dict__['_ReceivedResponses__instance'] = ReceivedResponses.__instance
00091 
00092     def __getattr__(self, attr):
00093         """ Delegate access to implementation """
00094         return getattr(self.__instance, attr)
00095 
00096     def __setattr__(self, attr, value):
00097         """ Delegate access to implementation """
00098         return setattr(self.__instance, attr, value)
00099 
00100 
00101 # instances of this class are created for every externally advertised service
00102 class ROS_Service_Template( threading.Thread):
00103     service_request_timeout = 600           #seconds
00104     check_response_delay = 0.1              #seconds
00105     wait_for_busy_service_provider = 0.1    #seconds
00106 
00107     max_requests = 500000
00108 
00109     service_name = None
00110     service_node_name = None
00111     service_module = None
00112     service_type = None
00113     client_callback = None
00114 
00115     client_id = None
00116     service_id = None
00117 
00118     ros_serviceproxy = None
00119 
00120     request_counter = 0
00121 
00122     response_list = ReceivedResponses().list    # holds service_responses until they are sent back to ROS-client
00123     protocol = None
00124 
00125     finish_flag = False
00126     busy = False
00127     spawned = False
00128 
00129     def __init__(self, client_callback, service_module, service_type, service_name, client_id, protocol):
00130         threading.Thread.__init__(self)
00131 
00132         self.protocol = protocol
00133         self.service_name = service_name
00134         self.service_module = service_module
00135         self.service_type = service_type
00136         self.client_id = client_id
00137         self.client_callback = client_callback
00138 
00139         # populate parameters
00140         if self.protocol.parameters != None:
00141             self.service_request_timeout  = self.protocol.parameters["service_request_timeout"]
00142             self.check_response_delay = self.protocol.parameters["check_response_delay"]
00143             self.wait_for_busy_service_provider = self.protocol.parameters["wait_for_busy_service_provider"]
00144             self.max_requests = self.protocol.parameters["max_service_requests"]
00145 
00146         self.spawn_ROS_service( service_module, service_type, service_name, client_id)
00147 
00148 
00149     def handle_service_request(self, req):
00150         # stay in this loop until (this) instance is not waiting for response for an "old" request
00151         # (.. probably not best solution so far)
00152         while not self.spawned or self.busy:
00153             # if stop_service was called.. 
00154             if self.finish_flag:
00155                 # kill unsent requests to that service
00156                 return None
00157             time.sleep(self.wait_for_busy_service_provider)
00158 
00159         self.busy = True
00160 
00161         # generate request_id
00162         # ..service_name avoids having same id's for different service-requests
00163         request_id = "service:" + self.service_name + "_count:" + str(self.request_counter) + "_time:" + datetime.now().strftime("%H:%M:%f")
00164         # increment request_counter
00165         self.request_counter = (self.request_counter + 1) % self.max_requests
00166 
00167         req_extracted = message_conversion.extract_values(req)
00168         request_message_object = {"op":"service_request",
00169                                   "request_id": request_id,
00170                                   "args": req_extracted
00171                                  }
00172                                     
00173         # add request to request_list
00174         if request_id not in self.protocol.request_list.keys():
00175             # put information about request into request_list, we need this later to create a response instance with service-module and -type
00176             self.protocol.request_list[request_id] = { "service_name" : self.service_name,
00177                                                        "service_module" : self.service_module,
00178                                                        "service_type" : self.service_type
00179                                                      }
00180         # answer will be passed to client that requested service
00181         answer = None
00182         try:
00183             # send JSON-service request to client that is providing the service (via protocol)
00184             self.client_callback( request_message_object )
00185             begin = datetime.now()
00186             duration = datetime.now() - begin
00187             # wait for service response by checking response_list
00188             # ..if stop_service was called.. stop waiting for response
00189             while not self.finish_flag and request_id not in self.response_list.keys() and duration.total_seconds() < self.service_request_timeout:
00190                 time.sleep(self.check_response_delay)
00191                 duration = datetime.now() - begin
00192             # if response found..
00193             if request_id in self.response_list:
00194                 answer = self.response_list[request_id]
00195                 # remove response from response_list
00196                 del self.response_list[request_id]
00197             else:
00198                 # request failed due to timeout (or some other reason?!)
00199                 print "request timed out!"
00200                 answer = None
00201             # remove request from request_list
00202             del self.protocol.request_list[request_id]
00203         # TODO: more detailed exception handling
00204         except Exception, e:
00205             print e
00206         self.busy = False
00207         return answer
00208 
00209     def stop_ROS_service(self):
00210         self.spawned = False
00211         self.finish_flag = True
00212         self.ros_serviceproxy.shutdown("reason: stop_service requested by providing client")
00213         # set answer for unfinished requests to None
00214         # --> response can be found, but will be the same as for failed/timed out requests..
00215         for request_id in self.protocol.request_list.keys():
00216             self.protocol.response_list[request_id] = None
00217         # wait for request_loops to run into finish_flags
00218         while len(self.protocol.request_list) > 0:
00219             time.sleep(self.check_response_delay)
00220         # remove service from service_list
00221 #        service_list = ServiceList().list
00222         del self.protocol.service_list[self.service_name]
00223         self.protocol.log("info", "removed service: "+ self.service_name)
00224     
00225     def spawn_ROS_service(self, service_module, service_type, service_name, client_id):
00226         # import service type for ros-service that we want to register in ros
00227         service_class = get_service_class(service_module+'/'+service_type)
00228         # register service in ros
00229         self.ros_serviceproxy = rospy.Service( service_name, service_class, self.handle_service_request)
00230 
00231         # TODO: check if service successfully registered in ros. current state is that rosbridge "thinks" service is registered, but calls will fail
00232 #        myServiceManager = rospy.service.ServiceManager()
00233 #        myServiceManager.register(service_name, self.ros_serviceproxy)
00234 #        print myServiceManager.get_services()
00235 
00236         # log service registration
00237         log_msg = "registered service: " + self.service_name
00238         self.protocol.log("info", log_msg)
00239         self.spawned = True
00240 
00241 class AdvertiseService(Capability):
00242 
00243     opcode_advertise_service = "advertise_service"      # rosbridge-client -> rosbridge # register in protocol.py!
00244     service_list = ServiceList().list                   # links to singleton
00245     request_list = RequestList().list     # holds requests until they are answered (response successfully sent to ROS-client)
00246 
00247     def __init__(self, protocol):
00248         self.protocol = protocol
00249         Capability.__init__(self, protocol)
00250         protocol.register_operation(self.opcode_advertise_service, self.advertise_service)
00251         self.protocol.service_list = self.service_list
00252         self.protocol.request_list = self.request_list
00253 
00254     def advertise_service(self, message):
00255         opcode = message["op"]
00256         service_type = message["service_type"]
00257         service_name = message["service_name"]
00258         service_module = message["service_module"]
00259         client_id = self.protocol.client_id
00260         client_callback = self.protocol.send
00261         # this part defines what is happening when a client is trying to "replace" an already registered service
00262         if service_name not in self.service_list.keys():
00263             self.service_list[service_name] = ROS_Service_Template(client_callback , service_module, service_type, service_name, client_id, self.protocol)
00264         else:
00265             log_msg = "is replacing service: " + service_name + " [provided before by client:" + str(self.service_list[service_name].client_id) + "]"
00266             self.protocol.log("info", log_msg)
00267             self.service_list[service_name].stop_ROS_service()
00268             self.service_list[service_name] = ROS_Service_Template(client_callback , service_module, service_type, service_name, client_id, self.protocol )
00269 
00270     def finish(self):
00271         self.finish_flag = True
00272         self.protocol.unregister_operation("advertise_service")


rosbridge_library
Author(s): Jonathan Mace
autogenerated on Mon Oct 6 2014 06:58:09