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)
def register_operation(self, opcode, handler)
def unregister_operation(self, opcode)
def __init__(self, client_id)
def add_capability(self, capability_class)
def deserialize(self, msg, cid=None)
int delay_between_messages
def outgoing(self, message)
def log(self, level, message, lid=None)
def send(self, message, cid=None, compression="none")
def incoming(self, message_string="")
def serialize(self, msg, cid=None)