From ee83ffd03364914d97f2ad785c64f7406c179302 Mon Sep 17 00:00:00 2001 From: deepanshu Date: Fri, 1 Jul 2022 16:05:06 -0400 Subject: [PATCH 1/4] Deserialize bytes type ata and expose options for the same Signed-off-by: deepanshu --- rosidl_runtime_py/convert.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/rosidl_runtime_py/convert.py b/rosidl_runtime_py/convert.py index eee4e37..6e817e7 100644 --- a/rosidl_runtime_py/convert.py +++ b/rosidl_runtime_py/convert.py @@ -21,6 +21,7 @@ import rosidl_parser.definition import yaml +from rclpy.serialization import deserialize_message __yaml_representer_registered = False @@ -69,7 +70,8 @@ def message_to_yaml( truncate_length: int = None, no_arr: bool = False, no_str: bool = False, - flow_style: bool = False + flow_style: bool = False, + serialize_msg_type: Any = None, ) -> str: """ Convert a ROS message to a YAML string. @@ -79,6 +81,8 @@ def message_to_yaml( This does not truncate the list of message fields. :param no_arr: Exclude array fields of the message. :param no_str: Exclude string fields of the message. + :param flow_style: Print collections in the block style. + :param serialize_msg_type: The ROS msg type for the message to be deserialized. :returns: A YAML string representation of the input ROS message. """ global __yaml_representer_registered @@ -90,7 +94,8 @@ def message_to_yaml( return yaml.dump( message_to_ordereddict( - msg, truncate_length=truncate_length, no_arr=no_arr, no_str=no_str), + msg, truncate_length=truncate_length, + no_arr=no_arr, no_str=no_str, serialize_msg_type=serialize_msg_type), allow_unicode=True, width=sys.maxsize, default_flow_style=flow_style, ) @@ -160,7 +165,8 @@ def message_to_ordereddict( *, truncate_length: int = None, no_arr: bool = False, - no_str: bool = False + no_str: bool = False, + serialize_msg_type: Any = None ) -> OrderedDict: """ Convert a ROS message to an OrderedDict. @@ -170,6 +176,7 @@ def message_to_ordereddict( This does not truncate the list of fields (ie. the dictionary keys). :param no_arr: Exclude array fields of the message. :param no_str: Exclude string fields of the message. + :param serialize_msg_type: The ROS msg type for the message to be deserialized. :returns: An OrderedDict where the keys are the ROS message fields and the values are set to the values of the input message. """ @@ -179,11 +186,21 @@ def message_to_ordereddict( for field_name, field_type in zip(msg.__slots__, msg.SLOT_TYPES): value = getattr(msg, field_name, None) - value = _convert_value( + converted_value = _convert_value( value, field_type=field_type, truncate_length=truncate_length, no_arr=no_arr, no_str=no_str) + + # Check if any of the data is of bytes type and deserialization is allowed. + if isinstance(value, (bytes, bytearray)) or \ + (isinstance(value, list) and any(isinstance(val, bytes) for val in value)): + if serialize_msg_type is not None: + deserialized_data = deserialize_message(b''.join(value), + serialize_msg_type) + converted_value = message_to_ordereddict( + deserialized_data, truncate_length=truncate_length, no_arr=no_arr, no_str=no_str) + # Remove leading underscore from field name - d[field_name[1:]] = value + d[field_name[1:]] = converted_value return d @@ -195,7 +212,6 @@ def _convert_value( no_arr=False, no_str=False ): - if isinstance(value, bytes): if truncate_length is not None and len(value) > truncate_length: value = ''.join([chr(c) for c in value[:truncate_length]]) + '...' From f7c048da6ad77442ad37e55eca4afd4d90b442a3 Mon Sep 17 00:00:00 2001 From: deepanshu Date: Sat, 2 Jul 2022 12:34:21 -0400 Subject: [PATCH 2/4] handle deserialization for msg to csv conversion Signed-off-by: deepanshu --- rosidl_runtime_py/convert.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/rosidl_runtime_py/convert.py b/rosidl_runtime_py/convert.py index 6e817e7..8c8c3e8 100644 --- a/rosidl_runtime_py/convert.py +++ b/rosidl_runtime_py/convert.py @@ -105,7 +105,8 @@ def message_to_csv( *, truncate_length: int = None, no_arr: bool = False, - no_str: bool = False + no_str: bool = False, + serialize_msg_type: Any = None, ) -> str: """ Convert a ROS message to string of comma-separated values. @@ -115,8 +116,10 @@ def message_to_csv( This does not truncate the list of message fields. :param no_arr: Exclude array fields of the message. :param no_str: Exclude string fields of the message. + :param serialize_msg_type: The ROS msg type for the message to be deserialized. :returns: A string of comma-separated values representing the input message. """ + def to_string(val, field_type=None): nonlocal truncate_length, no_arr, no_str r = '' @@ -154,7 +157,19 @@ def to_string(val, field_type=None): if result: result += ',' - result += to_string(value, field_type) + value_to_string = to_string(value, field_type) + + # Check if any of the data is of bytes type and deserialization is allowed. + if isinstance(value, (bytes, bytearray)) or \ + (isinstance(value, list) and any(isinstance(val, bytes) for val in value)): + if serialize_msg_type is not None: + deserialized_data = deserialize_message(b''.join(value), + serialize_msg_type) + value_to_string = message_to_csv( + deserialized_data, truncate_length=truncate_length, + no_arr=no_arr, no_str=no_str) + + result += value_to_string return result From e14877d3487ba9b3183f16f998daceae6b2ce38e Mon Sep 17 00:00:00 2001 From: deepanshu Date: Tue, 5 Jul 2022 14:54:31 -0400 Subject: [PATCH 3/4] Rename serialize_msg_type to derialize_msg_type Signed-off-by: deepanshu --- rosidl_runtime_py/convert.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/rosidl_runtime_py/convert.py b/rosidl_runtime_py/convert.py index 8c8c3e8..219c6bd 100644 --- a/rosidl_runtime_py/convert.py +++ b/rosidl_runtime_py/convert.py @@ -71,7 +71,7 @@ def message_to_yaml( no_arr: bool = False, no_str: bool = False, flow_style: bool = False, - serialize_msg_type: Any = None, + deserialize_msg_type: Any = None, ) -> str: """ Convert a ROS message to a YAML string. @@ -82,7 +82,7 @@ def message_to_yaml( :param no_arr: Exclude array fields of the message. :param no_str: Exclude string fields of the message. :param flow_style: Print collections in the block style. - :param serialize_msg_type: The ROS msg type for the message to be deserialized. + :param deserialize_msg_type: The ROS msg type for the message to be deserialized. :returns: A YAML string representation of the input ROS message. """ global __yaml_representer_registered @@ -95,7 +95,7 @@ def message_to_yaml( return yaml.dump( message_to_ordereddict( msg, truncate_length=truncate_length, - no_arr=no_arr, no_str=no_str, serialize_msg_type=serialize_msg_type), + no_arr=no_arr, no_str=no_str, deserialize_msg_type=deserialize_msg_type), allow_unicode=True, width=sys.maxsize, default_flow_style=flow_style, ) @@ -106,7 +106,7 @@ def message_to_csv( truncate_length: int = None, no_arr: bool = False, no_str: bool = False, - serialize_msg_type: Any = None, + deserialize_msg_type: Any = None, ) -> str: """ Convert a ROS message to string of comma-separated values. @@ -116,7 +116,7 @@ def message_to_csv( This does not truncate the list of message fields. :param no_arr: Exclude array fields of the message. :param no_str: Exclude string fields of the message. - :param serialize_msg_type: The ROS msg type for the message to be deserialized. + :param deserialize_msg_type: The ROS msg type for the message to be deserialized. :returns: A string of comma-separated values representing the input message. """ @@ -162,9 +162,9 @@ def to_string(val, field_type=None): # Check if any of the data is of bytes type and deserialization is allowed. if isinstance(value, (bytes, bytearray)) or \ (isinstance(value, list) and any(isinstance(val, bytes) for val in value)): - if serialize_msg_type is not None: + if deserialize_msg_type is not None: deserialized_data = deserialize_message(b''.join(value), - serialize_msg_type) + deserialize_msg_type) value_to_string = message_to_csv( deserialized_data, truncate_length=truncate_length, no_arr=no_arr, no_str=no_str) @@ -181,7 +181,7 @@ def message_to_ordereddict( truncate_length: int = None, no_arr: bool = False, no_str: bool = False, - serialize_msg_type: Any = None + deserialize_msg_type: Any = None ) -> OrderedDict: """ Convert a ROS message to an OrderedDict. @@ -191,7 +191,7 @@ def message_to_ordereddict( This does not truncate the list of fields (ie. the dictionary keys). :param no_arr: Exclude array fields of the message. :param no_str: Exclude string fields of the message. - :param serialize_msg_type: The ROS msg type for the message to be deserialized. + :param deserialize_msg_type: The ROS msg type for the message to be deserialized. :returns: An OrderedDict where the keys are the ROS message fields and the values are set to the values of the input message. """ @@ -206,11 +206,12 @@ def message_to_ordereddict( truncate_length=truncate_length, no_arr=no_arr, no_str=no_str) # Check if any of the data is of bytes type and deserialization is allowed. + # TODO: check if successful if isinstance(value, (bytes, bytearray)) or \ (isinstance(value, list) and any(isinstance(val, bytes) for val in value)): - if serialize_msg_type is not None: + if deserialize_msg_type is not None: deserialized_data = deserialize_message(b''.join(value), - serialize_msg_type) + deserialize_msg_type) converted_value = message_to_ordereddict( deserialized_data, truncate_length=truncate_length, no_arr=no_arr, no_str=no_str) From ecc1419942ce899dec7bce59e29a5312ef174525 Mon Sep 17 00:00:00 2001 From: deepanshu Date: Tue, 5 Jul 2022 16:28:19 -0400 Subject: [PATCH 4/4] handle exceptions during deserialization Signed-off-by: deepanshu --- rosidl_runtime_py/convert.py | 38 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/rosidl_runtime_py/convert.py b/rosidl_runtime_py/convert.py index 219c6bd..c8d3cd2 100644 --- a/rosidl_runtime_py/convert.py +++ b/rosidl_runtime_py/convert.py @@ -13,15 +13,17 @@ # limitations under the License. import array -from collections import OrderedDict import sys +from collections import OrderedDict from typing import Any import numpy import rosidl_parser.definition import yaml +from rclpy.exceptions import NoTypeSupportImportedException from rclpy.serialization import deserialize_message +from rclpy.impl.implementation_singleton import rclpy_implementation as _rclpy __yaml_representer_registered = False @@ -64,6 +66,21 @@ def __represent_ordereddict(dumper, data): return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', items) +def __deserialize_message(value, deserialize_msg_type): + deserialized_data = None + try: + deserialized_data = deserialize_message(b''.join(value), + deserialize_msg_type) + except (AttributeError, NoTypeSupportImportedException): + print('WARNING: The deserialize message type [%s] ' + 'provided is not valid, skipping deserialization' % deserialize_msg_type) + except _rclpy.RMWError as error: + print('WARNING: Failed to deserialize ROS message, ' + 'this might be due to incorrect deserialize message type') + + return deserialized_data + + def message_to_yaml( msg: Any, *, @@ -163,11 +180,11 @@ def to_string(val, field_type=None): if isinstance(value, (bytes, bytearray)) or \ (isinstance(value, list) and any(isinstance(val, bytes) for val in value)): if deserialize_msg_type is not None: - deserialized_data = deserialize_message(b''.join(value), - deserialize_msg_type) - value_to_string = message_to_csv( - deserialized_data, truncate_length=truncate_length, - no_arr=no_arr, no_str=no_str) + deserialized_data = __deserialize_message(value, deserialize_msg_type) + if deserialized_data is not None: + value_to_string = message_to_csv( + deserialized_data, truncate_length=truncate_length, + no_arr=no_arr, no_str=no_str) result += value_to_string return result @@ -206,14 +223,13 @@ def message_to_ordereddict( truncate_length=truncate_length, no_arr=no_arr, no_str=no_str) # Check if any of the data is of bytes type and deserialization is allowed. - # TODO: check if successful if isinstance(value, (bytes, bytearray)) or \ (isinstance(value, list) and any(isinstance(val, bytes) for val in value)): if deserialize_msg_type is not None: - deserialized_data = deserialize_message(b''.join(value), - deserialize_msg_type) - converted_value = message_to_ordereddict( - deserialized_data, truncate_length=truncate_length, no_arr=no_arr, no_str=no_str) + deserialized_data = __deserialize_message(value, deserialize_msg_type) + if deserialized_data is not None: + converted_value = message_to_ordereddict( + deserialized_data, truncate_length=truncate_length, no_arr=no_arr, no_str=no_str) # Remove leading underscore from field name d[field_name[1:]] = converted_value