33 from __future__
import unicode_literals
34 from builtins
import str
55 """ Returns True if obj is a binary or contains a binary attribute
58 if isinstance(obj, list):
61 if isinstance(obj, dict):
62 return any(
has_binary(obj[item])
for item
in obj)
64 return isinstance(obj, bson.binary.Binary)
68 """ The interface for a single client to interact with ROS.
70 See rosbridge_protocol for the default protocol used by rosbridge
72 The lifecycle for a Protocol instance is as follows:
73 - Pass incoming messages from the client to incoming
74 - Propagate outgoing messages to the client by overriding outgoing
75 - Call finish to clean up resources when the client is finished
90 delay_between_messages = 0
92 external_service_list = {}
94 bson_only_mode =
False
99 """ Keyword arguments:
100 client_id -- a unique ID for this client to take. Uniqueness is
101 important otherwise there will be conflicts between multiple clients
102 with shared resources
121 """ Process an incoming message from the client
124 message_string -- the wire-level message sent by the client
128 self.
buffer.extend(message_string)
144 except Exception
as e:
150 self.
log(
"error",
"Exception in deserialization of BSON")
165 opening_brackets = [i
for i, letter
in enumerate(self.
buffer)
if letter ==
'{']
166 closing_brackets = [i
for i, letter
in enumerate(self.
buffer)
if letter ==
'}']
168 for start
in opening_brackets:
169 for end
in closing_brackets:
172 if msg.get(
"op",
None) !=
None:
177 except Exception
as e:
194 if "receiver" in msg:
195 self.
log(
"error",
"Received a rosbridge v1.0 message. Please refer to rosbridge.org for the correct format of rosbridge v2.0 messages. Original message was: %s" % message_string)
197 self.
log(
"error",
"Received a message without an op. All messages require 'op' field with value one of: %s. Original message was: %s" % (list(self.
operations.keys()), message_string), mid)
201 self.
log(
"error",
"Unknown operation: %s. Allowed operations: %s" % (op, list(self.
operations.keys())), mid)
205 if "fragment_size" in msg.keys():
208 if "message_intervall" in msg.keys()
and is_number(msg[
"message_intervall"]):
210 if "png" in msg.keys():
211 self.
png = msg[
"msg"]
216 except Exception
as exc:
217 self.
log(
"error",
"%s: %s" % (op, str(exc)), mid)
230 """ Pass an outgoing message to the client. This method should be
234 message -- the wire-level message to send to the client
239 def send(self, message, cid=None, compression="none"):
240 """ Called internally in preparation for sending messages to the client
242 This method pre-processes the message then passes it to the overridden
246 message -- a dict of message values to be marshalled and sent
247 cid -- (optional) an associated id
250 serialized = message
if compression
in [
"cbor",
"cbor-raw"]
else self.serialize(message, cid)
251 if serialized
is not None:
252 if self.png ==
"png":
258 if self.fragment_size !=
None and len(serialized) > self.fragment_size:
259 mid = message.get(
"id",
None)
263 fragment_list = Fragmentation(self).
fragment(message, self.fragment_size, mid )
266 if fragment_list !=
None:
267 for fragment
in fragment_list:
268 if self.bson_only_mode:
269 self.outgoing(bson.BSON.encode(fragment), compression=compression)
271 self.outgoing(json.dumps(fragment), compression=compression)
274 time.sleep(self.delay_between_messages)
277 self.outgoing(serialized, compression=compression)
278 time.sleep(self.delay_between_messages)
281 """ Indicate that the client is finished and clean up resources.
283 All clients should call this method after disconnecting.
290 """ Turns a dictionary of values into the appropriate wire-level
293 Default behaviour uses JSON. Override to use a different container.
296 msg -- the dictionary of values to serialize
297 cid -- (optional) an ID associated with this. Will be logged on err.
299 Returns a JSON string representing the dictionary
302 if type(msg) == bytearray:
305 return bson.BSON.encode(msg)
307 return json.dumps(msg)
311 self.
log(
"error",
"Unable to serialize %s message to client"
317 """ Turns the wire-level representation into a dictionary of values
319 Default behaviour assumes JSON. Override to use a different container.
322 msg -- the wire-level message to deserialize
323 cid -- (optional) an ID associated with this. Is logged on error
325 Returns a dictionary of values
330 bson_message = bson.BSON(msg)
331 return bson_message.decode()
333 return json.loads(msg)
334 except Exception
as e:
352 """ Register a handler for an opcode
355 opcode -- the opcode to register this handler for
356 handler -- a callback function to call for messages with this opcode
362 """ Unregister a handler for an opcode
365 opcode -- the opcode to unregister the handler for
372 """ Add a capability to the protocol.
374 This method is for convenience; assumes the default capability
378 capability_class -- the class of the capability to add
383 def log(self, level, message, lid=None):
384 """ Log a message to the client. By default just sends to stdout
387 level -- the logger level of this message
388 message -- the string message to send to the user
389 lid -- an associated for this log message
392 stdout_formatted_msg =
None
394 stdout_formatted_msg =
"[Client %s] [id: %s] %s" % (self.
client_id, lid, message)
396 stdout_formatted_msg =
"[Client %s] %s" % (self.
client_id, message)
398 if level ==
"error" or level ==
"err":
399 rospy.logerr(stdout_formatted_msg)
400 elif level ==
"warning" or level ==
"warn":
401 rospy.logwarn(stdout_formatted_msg)
402 elif level ==
"info" or level ==
"information":
403 rospy.loginfo(stdout_formatted_msg)
405 rospy.logdebug(stdout_formatted_msg)