Skip to content

Commit f03938e

Browse files
authored
[ros2topic] Add option to echo serialized messages (#470)
I found this feature useful when doing some debugging. Signed-off-by: Jacob Perron <[email protected]>
1 parent acb13d9 commit f03938e

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

ros2topic/ros2topic/verb/echo.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ def add_arguments(self, parser, cli_name):
7474
'--no-str', action='store_true', help="Don't print string fields of messages")
7575
parser.add_argument(
7676
'--lost-messages', action='store_true', help='Report when a message is lost')
77+
parser.add_argument(
78+
'--raw', action='store_true', help='Echo the raw binary representation')
7779

7880
def main(self, *, args):
7981
return main(args)
@@ -101,7 +103,13 @@ def main(args):
101103
'Could not determine the type for the passed topic')
102104

103105
subscriber(
104-
node, args.topic_name, message_type, callback, qos_profile, args.lost_messages)
106+
node,
107+
args.topic_name,
108+
message_type,
109+
callback,
110+
qos_profile,
111+
args.lost_messages,
112+
args.raw)
105113

106114

107115
def subscriber(
@@ -110,7 +118,8 @@ def subscriber(
110118
message_type: MsgType,
111119
callback: Callable[[MsgType], Any],
112120
qos_profile: QoSProfile,
113-
report_lost_messages: bool
121+
report_lost_messages: bool,
122+
raw: bool
114123
) -> Optional[str]:
115124
"""Initialize a node with a single subscription and spin."""
116125
event_callbacks = None
@@ -119,7 +128,12 @@ def subscriber(
119128
message_lost=message_lost_event_callback)
120129
try:
121130
node.create_subscription(
122-
message_type, topic_name, callback, qos_profile, event_callbacks=event_callbacks)
131+
message_type,
132+
topic_name,
133+
callback,
134+
qos_profile,
135+
event_callbacks=event_callbacks,
136+
raw=raw)
123137
except UnsupportedEventTypeError:
124138
assert report_lost_messages
125139
print(
@@ -132,18 +146,23 @@ def subscriber(
132146
def subscriber_cb(truncate_length, noarr, nostr):
133147
def cb(msg):
134148
nonlocal truncate_length, noarr, nostr
135-
print(
136-
message_to_yaml(
137-
msg, truncate_length=truncate_length, no_arr=noarr, no_str=nostr),
138-
end='---\n')
149+
if isinstance(msg, bytes):
150+
print(msg, end='\n---\n')
151+
else:
152+
print(
153+
message_to_yaml(
154+
msg, truncate_length=truncate_length, no_arr=noarr, no_str=nostr),
155+
end='---\n')
139156
return cb
140157

141158

142159
def subscriber_cb_csv(truncate_length, noarr, nostr):
143160
def cb(msg):
144161
nonlocal truncate_length, noarr, nostr
145-
print(message_to_csv(msg, truncate_length=truncate_length,
146-
no_arr=noarr, no_str=nostr))
162+
if isinstance(msg, bytes):
163+
print(msg)
164+
else:
165+
print(message_to_csv(msg, truncate_length=truncate_length, no_arr=noarr, no_str=nostr))
147166
return cb
148167

149168

ros2topic/test/test_echo_pub.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import functools
1516
import sys
1617
import unittest
1718

@@ -309,3 +310,45 @@ def publish_message():
309310
# Cleanup
310311
self.node.destroy_timer(publish_timer)
311312
self.node.destroy_publisher(publisher)
313+
314+
@launch_testing.markers.retry_on_failure(times=5)
315+
def test_echo_raw(self, launch_service, proc_info, proc_output):
316+
topic = '/clitest/topic/echo_raw'
317+
publisher = self.node.create_publisher(String, topic, 10)
318+
assert publisher
319+
320+
def publish_message():
321+
publisher.publish(String(data='hello'))
322+
323+
publish_timer = self.node.create_timer(0.5, publish_message)
324+
325+
try:
326+
command_action = ExecuteProcess(
327+
cmd=['ros2', 'topic', 'echo', '--raw', topic, 'std_msgs/msg/String'],
328+
additional_env={
329+
'PYTHONUNBUFFERED': '1'
330+
},
331+
output='screen'
332+
)
333+
with launch_testing.tools.launch_process(
334+
launch_service, command_action, proc_info, proc_output,
335+
output_filter=launch_testing_ros.tools.basic_output_filter(
336+
filtered_rmw_implementation=get_rmw_implementation_identifier()
337+
)
338+
) as command:
339+
# The future won't complete - we will hit the timeout
340+
self.executor.spin_until_future_complete(
341+
rclpy.task.Future(), timeout_sec=5
342+
)
343+
assert command.wait_for_output(functools.partial(
344+
launch_testing.tools.expect_output, expected_lines=[
345+
"b'\\x00\\x01\\x00\\x00\\x06\\x00\\x00\\x00hello\\x00\\x00\\x00'",
346+
'---',
347+
], strict=True
348+
), timeout=10), 'Echo CLI did not print expected message'
349+
assert command.wait_for_shutdown(timeout=10)
350+
351+
finally:
352+
# Cleanup
353+
self.node.destroy_timer(publish_timer)
354+
self.node.destroy_publisher(publisher)

0 commit comments

Comments
 (0)