1818from io import BytesIO
1919
2020import threading
21+ import json
2122
2223from .exceptions import TopicOrServiceNameDoesNotExistError
23- from ros_tcp_endpoint .msg import RosUnitySrvMessage
2424
2525
2626class ClientThread (threading .Thread ):
@@ -145,6 +145,47 @@ def serialize_message(destination, message):
145145
146146 return serialized_message
147147
148+ @staticmethod
149+ def serialize_command (command , params ):
150+ cmd_bytes = command .encode ("utf-8" )
151+ cmd_length = len (cmd_bytes )
152+ cmd_info = struct .pack ("<I%ss" % cmd_length , cmd_length , cmd_bytes )
153+
154+ json_bytes = json .dumps (params .__dict__ ).encode ("utf-8" )
155+ json_length = len (json_bytes )
156+ json_info = struct .pack ("<I%ss" % json_length , json_length , json_bytes )
157+
158+ return cmd_info + json_info
159+
160+ def send_ros_service_request (self , srv_id , destination , data ):
161+ if destination not in self .tcp_server .source_destination_dict .keys ():
162+ error_msg = "Service destination '{}' is not registered! Known topics are: {} " .format (
163+ destination , self .tcp_server .source_destination_dict .keys ()
164+ )
165+ self .tcp_server .send_unity_error (error_msg )
166+ rospy .logerr (error_msg )
167+ # TODO: send a response to Unity anyway?
168+ return
169+ else :
170+ ros_communicator = self .tcp_server .source_destination_dict [destination ]
171+ service_thread = threading .Thread (
172+ target = self .service_call_thread , args = (srv_id , destination , data , ros_communicator )
173+ )
174+ service_thread .daemon = True
175+ service_thread .start ()
176+
177+ def service_call_thread (self , srv_id , destination , data , ros_communicator ):
178+ response = ros_communicator .send (data )
179+
180+ if not response :
181+ error_msg = "No response data from service '{}'!" .format (destination )
182+ self .tcp_server .send_unity_error (error_msg )
183+ rospy .logerr (error_msg )
184+ # TODO: send a response to Unity anyway?
185+ return
186+
187+ self .tcp_server .unity_tcp_sender .send_ros_service_response (srv_id , destination , response )
188+
148189 def run (self ):
149190 """
150191 Read a message and determine where to send it based on the source_destination_dict
@@ -167,50 +208,23 @@ def run(self):
167208 while not halt_event .is_set ():
168209 destination , data = self .read_message (self .conn )
169210
170- if destination == "" :
211+ if self .tcp_server .pending_srv_id is not None :
212+ # if we've been told that the next message will be a service request/response, process it as such
213+ if self .tcp_server .pending_srv_is_request :
214+ self .send_ros_service_request (
215+ self .tcp_server .pending_srv_id , destination , data
216+ )
217+ else :
218+ self .tcp_server .send_unity_service_response (
219+ self .tcp_server .pending_srv_id , data
220+ )
221+ self .tcp_server .pending_srv_id = None
222+ elif destination == "" :
171223 # ignore this keepalive message, listen for more
172224 pass
173- elif destination == "__syscommand" :
225+ elif destination . startswith ( "__" ) :
174226 # handle a system command, such as registering new topics
175- self .tcp_server .handle_syscommand (data )
176- elif destination == "__srv" :
177- # handle a ros service message request, or a unity service message response
178- srv_message = RosUnitySrvMessage ().deserialize (data )
179- if not srv_message .is_request :
180- self .tcp_server .send_unity_service_response (
181- srv_message .srv_id , srv_message .payload
182- )
183- continue
184- elif srv_message .topic == "__topic_list" :
185- response = self .tcp_server .topic_list (data )
186- elif srv_message .topic not in self .tcp_server .source_destination_dict .keys ():
187- error_msg = "Service destination '{}' is not registered! Known topics are: {} " .format (
188- srv_message .topic , self .tcp_server .source_destination_dict .keys ()
189- )
190- self .tcp_server .send_unity_error (error_msg )
191- rospy .logerr (error_msg )
192- # TODO: send a response to Unity anyway?
193- continue
194- else :
195- ros_communicator = self .tcp_server .source_destination_dict [
196- srv_message .topic
197- ]
198- response = ros_communicator .send (srv_message .payload )
199- if not response :
200- error_msg = "No response data from service '{}'!" .format (
201- srv_message .topic
202- )
203- self .tcp_server .send_unity_error (error_msg )
204- rospy .logerr (error_msg )
205- # TODO: send a response to Unity anyway?
206- continue
207-
208- serial_response = BytesIO ()
209- response .serialize (serial_response )
210- response_message = RosUnitySrvMessage (
211- srv_message .srv_id , False , "" , serial_response .getvalue ()
212- )
213- self .tcp_server .unity_tcp_sender .send_unity_message ("__srv" , response_message )
227+ self .tcp_server .handle_syscommand (destination , data )
214228 elif destination in self .tcp_server .source_destination_dict :
215229 ros_communicator = self .tcp_server .source_destination_dict [destination ]
216230 response = ros_communicator .send (data )
0 commit comments