Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ros2service/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<exec_depend>python3-yaml</exec_depend>
<exec_depend>ros2srv</exec_depend>
<exec_depend>ros2topic</exec_depend>
<exec_depend>rosidl_runtime_py</exec_depend>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
Expand Down
10 changes: 4 additions & 6 deletions ros2service/ros2service/verb/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
from ros2service.api import ServiceNameCompleter
from ros2service.api import ServiceTypeCompleter
from ros2service.verb import VerbExtension
from ros2topic.api import set_msg_fields
from ros2topic.api import SetFieldError
from rosidl_runtime_py import set_message_fields
import yaml


Expand Down Expand Up @@ -84,10 +83,9 @@ def requester(service_type, service_name, values, period):
request = srv_module.Request()

try:
set_msg_fields(request, values_dictionary)
except SetFieldError as e: # noqa: F841
return "Failed to populate field '{e.field_name}': {e.exception}" \
.format_map(locals())
set_message_fields(request, values_dictionary)
except Exception as e:
return 'Failed to populate field: {0}'.format(e)

if not cli.service_is_ready():
print('waiting for service to become available...')
Expand Down
1 change: 1 addition & 0 deletions ros2topic/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<exec_depend>python3-yaml</exec_depend>
<exec_depend>rclpy</exec_depend>
<exec_depend>ros2msg</exec_depend>
<exec_depend>rosidl_runtime_py</exec_depend>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
Expand Down
29 changes: 0 additions & 29 deletions ros2topic/ros2topic/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,35 +90,6 @@ def __call__(self, prefix, parsed_args, **kwargs):
return message_type_completer()


class SetFieldError(Exception):

def __init__(self, field_name, exception):
super(SetFieldError, self).__init__()
self.field_name = field_name
self.exception = exception


def set_msg_fields(msg, values):
for field_name, field_value in values.items():
field_type = type(getattr(msg, field_name))
try:
value = field_type(field_value)
except TypeError:
value = field_type()
try:
set_msg_fields(value, field_value)
except SetFieldError as e:
raise SetFieldError(
'{field_name}.{e.field_name}'.format_map(locals()),
e.exception)
except ValueError as e:
raise SetFieldError(field_name, e)
try:
setattr(msg, field_name, value)
except Exception as e:
raise SetFieldError(field_name, e)


def get_msg_class(node, topic, blocking=False, include_hidden_topics=False):
msg_class = _get_msg_class(node, topic, include_hidden_topics)
if msg_class:
Expand Down
142 changes: 10 additions & 132 deletions ros2topic/ros2topic/verb/echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@
# limitations under the License.

from argparse import ArgumentTypeError
import array
from collections import OrderedDict
import sys

import numpy
import rclpy
from rclpy.expand_topic_name import expand_topic_name
from rclpy.qos import qos_profile_sensor_data
Expand All @@ -27,7 +23,8 @@
from ros2topic.api import import_message_type
from ros2topic.api import TopicNameCompleter
from ros2topic.verb import VerbExtension
import yaml
from rosidl_runtime_py import message_to_csv
from rosidl_runtime_py import message_to_yaml

DEFAULT_TRUNCATE_LENGTH = 128

Expand Down Expand Up @@ -74,31 +71,14 @@ def main(self, *, args):

def main(args):
if not args.csv:
register_yaml_representer()
callback = subscriber_cb(args)
callback = subscriber_cb(args.truncate_length if not args.full_length else None)
else:
callback = subscriber_cb_csv(args)
callback = subscriber_cb_csv(args.truncate_length if not args.full_length else None)
with DirectNode(args) as node:
subscriber(
node.node, args.topic_name, args.message_type, callback)


def register_yaml_representer():
# Register our custom representer for YAML output
yaml.add_representer(OrderedDict, represent_ordereddict)


# Custom representer for getting clean YAML output that preserves the order in
# an OrderedDict.
# Inspired by:
# http://stackoverflow.com/a/16782282/7169408
def represent_ordereddict(dumper, data):
items = []
for k, v in data.items():
items.append((dumper.represent_data(k), dumper.represent_data(v)))
return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', items)


def subscriber(node, topic_name, message_type, callback):
if message_type is None:
topic_names_and_types = get_topic_names_and_types(node=node, include_hidden_topics=True)
Expand Down Expand Up @@ -132,117 +112,15 @@ def subscriber(node, topic_name, message_type, callback):
rclpy.spin_once(node)


def subscriber_cb(args):
def subscriber_cb(truncate_length):
def cb(msg):
nonlocal args
print(msg_to_yaml(args, msg))
nonlocal truncate_length
print(message_to_yaml(msg, truncate_length))
return cb


def msg_to_yaml(args, msg):
return yaml.dump(
msg_to_ordereddict(
msg,
truncate_length=args.truncate_length if not args.full_length else None
), width=sys.maxsize)


def subscriber_cb_csv(args):
def subscriber_cb_csv(truncate_length):
def cb(msg):
nonlocal args
print(msg_to_csv(args, msg))
nonlocal truncate_length
print(message_to_csv(msg, truncate_length))
return cb


def msg_to_csv(args, msg):
def to_string(val):
nonlocal args
r = ''
if (any(
isinstance(val, t)
for t in [list, tuple, array.array, numpy.ndarray]
)):
for i, v in enumerate(val):
if r:
r += ','
if not args.full_length and i >= args.truncate_length:
r += '...'
break
r += to_string(v)
elif (any(
isinstance(val, t)
for t in [bool, bytes, float, int, str, numpy.number]
)):
if any(isinstance(val, t) for t in [bytes, str]):
if not args.full_length and len(val) > args.truncate_length:
val = val[:args.truncate_length]
if isinstance(val, bytes):
val += b'...'
else:
val += '...'
r = str(val)
else:
r = msg_to_csv(args, val)
return r
result = ''
# We rely on __slots__ retaining the order of the fields in the .msg file.
for field_name in msg.__slots__:
value = getattr(msg, field_name, None)
if result:
result += ','
result += to_string(value)
return result


# Convert a msg to an OrderedDict. We do this instead of implementing a generic
# __dict__() method in the msg because we want to preserve order of fields from
# the .msg file(s).
def msg_to_ordereddict(msg, truncate_length=None):
d = OrderedDict()
# We rely on __slots__ retaining the order of the fields in the .msg file.
for field_name in msg.__slots__:
value = getattr(msg, field_name, None)
value = _convert_value(value, truncate_length=truncate_length)
# remove leading underscore from field name
d[field_name[1:]] = value
return d


def _convert_value(value, truncate_length=None):
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]]) + '...'
else:
value = ''.join([chr(c) for c in value])
elif isinstance(value, str):
if truncate_length is not None and len(value) > truncate_length:
value = value[:truncate_length] + '...'
elif (any(
isinstance(value, t) for t in [list, tuple, array.array, numpy.ndarray]
)):
# since arrays and ndarrays can't contain mixed types convert to list
typename = tuple if isinstance(value, tuple) else list
if truncate_length is not None and len(value) > truncate_length:
# Truncate the sequence
value = value[:truncate_length]
# Truncate every item in the sequence
value = typename(
[_convert_value(v, truncate_length) for v in value] + ['...'])
else:
# Truncate every item in the list
value = typename(
[_convert_value(v, truncate_length) for v in value])
elif isinstance(value, dict) or isinstance(value, OrderedDict):
# convert each key and value in the mapping
new_value = {} if isinstance(value, dict) else OrderedDict()
for k, v in value.items():
# don't truncate keys because that could result in key collisions and data loss
new_value[_convert_value(k)] = _convert_value(v, truncate_length=truncate_length)
value = new_value
elif (
not any(isinstance(value, t) for t in (bool, float, int, numpy.number))
):
# assuming value is a message
# since it is neither a collection nor a primitive type
value = msg_to_ordereddict(value, truncate_length=truncate_length)
return value
11 changes: 5 additions & 6 deletions ros2topic/ros2topic/verb/pub.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
import rclpy
from ros2cli.node import NODE_NAME_PREFIX
from ros2topic.api import import_message_type
from ros2topic.api import set_msg_fields
from ros2topic.api import SetFieldError
from ros2topic.api import TopicNameCompleter
from ros2topic.api import TopicTypeCompleter
from ros2topic.verb import VerbExtension
from rosidl_runtime_py import set_message_fields

import yaml


Expand Down Expand Up @@ -87,10 +87,9 @@ def publisher(

msg = msg_module()
try:
set_msg_fields(msg, values_dictionary)
except SetFieldError as e: # noqa: F841
return "Failed to populate field '{e.field_name}': {e.exception}" \
.format_map(locals())
set_message_fields(msg, values_dictionary)
except Exception as e:
return 'Failed to populate field: {0}'.format(e)

print('publisher: beginning loop')
count = 0
Expand Down
85 changes: 0 additions & 85 deletions ros2topic/test/test_echo.py

This file was deleted.

Loading