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)