Skip to content

Commit d9bece4

Browse files
committed
Expand timestamps for std_msgs.msg.Header and builtin_interfaces.msg.Time if 'auto' and 'now' are passed as values
Signed-off-by: Esteve Fernandez <esteve.fernandez@tier4.jp>
1 parent 9bb27db commit d9bece4

File tree

1 file changed

+66
-32
lines changed

1 file changed

+66
-32
lines changed

rosidl_runtime_py/set_message.py

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
# limitations under the License.
1414

1515
import array
16+
from functools import partial
1617

1718
from typing import Any
1819
from typing import Dict
20+
from typing import List
1921

2022
import numpy
2123

@@ -25,44 +27,76 @@
2527
from rosidl_runtime_py.import_message import import_message_from_namespaced_type
2628

2729

28-
def set_message_fields(msg: Any, values: Dict[str, str]) -> None:
30+
def set_message_fields(
31+
msg: Any, values: Dict[str, str], expand_header_auto: bool = False,
32+
expand_time_now: bool = False) -> List[Any]:
2933
"""
3034
Set the fields of a ROS message.
3135
3236
:param msg: The ROS message to populate.
3337
:param values: The values to set in the ROS message. The keys of the dictionary represent
3438
fields of the message.
39+
:param expand_header_auto: If enabled and 'auto' is passed as a value to a
40+
'std_msgs.msg.Header' field, an empty Header will be instantiated and a setter function
41+
will be returned so that its 'stamp' field can be set to the current time.
42+
:param expand_time_now: If enabled and 'now' is passed as a value to a
43+
'builtin_interfaces.msg.Time' field, a setter function will be returned so that
44+
its value can be set to the current time.
45+
:returns: A list of setter functions that can be used to update 'builtin_interfaces.msg.Time'
46+
fields, useful for setting them to the current time. The list will be empty if the message
47+
does not have any 'builtin_interfaces.msg.Time' fields, or if expand_header_auto and
48+
expand_time_now are false.
3549
:raises AttributeError: If the message does not have a field provided in the input dictionary.
3650
:raises TypeError: If a message value does not match its field type.
3751
"""
38-
try:
39-
items = values.items()
40-
except AttributeError:
41-
raise TypeError(
42-
"Value '%s' is expected to be a dictionary but is a %s" %
43-
(values, type(values).__name__))
44-
for field_name, field_value in items:
45-
field = getattr(msg, field_name)
46-
field_type = type(field)
47-
if field_type is array.array:
48-
value = field_type(field.typecode, field_value)
49-
elif field_type is numpy.ndarray:
50-
value = numpy.array(field_value, dtype=field.dtype)
51-
elif type(field_value) is field_type:
52-
value = field_value
53-
else:
54-
try:
55-
value = field_type(field_value)
56-
except TypeError:
57-
value = field_type()
58-
set_message_fields(value, field_value)
59-
rosidl_type = get_message_slot_types(msg)[field_name]
60-
# Check if field is an array of ROS messages
61-
if isinstance(rosidl_type, AbstractNestedType):
62-
if isinstance(rosidl_type.value_type, NamespacedType):
63-
field_elem_type = import_message_from_namespaced_type(rosidl_type.value_type)
64-
for n in range(len(value)):
65-
submsg = field_elem_type()
66-
set_message_fields(submsg, value[n])
67-
value[n] = submsg
68-
setattr(msg, field_name, value)
52+
timestamp_fields = []
53+
54+
def set_message_fields_internal(
55+
msg: Any, values: Dict[str, str],
56+
timestamp_fields: List[Any]) -> List[Any]:
57+
try:
58+
items = values.items()
59+
except AttributeError:
60+
raise TypeError(
61+
"Value '%s' is expected to be a dictionary but is a %s" %
62+
(values, type(values).__name__))
63+
for field_name, field_value in items:
64+
field = getattr(msg, field_name)
65+
field_type = type(field)
66+
qualified_class_name = '{}.{}'.format(field_type.__module__, field_type.__name__)
67+
if field_type is array.array:
68+
value = field_type(field.typecode, field_value)
69+
elif field_type is numpy.ndarray:
70+
value = numpy.array(field_value, dtype=field.dtype)
71+
elif type(field_value) is field_type:
72+
value = field_value
73+
# We can't import these types directly, so we use the qualified class name to
74+
# distinguish them from other fields
75+
elif qualified_class_name == 'std_msgs.msg._header.Header' and \
76+
field_value == 'auto' and expand_header_auto:
77+
timestamp_fields.append(partial(setattr, field, 'stamp'))
78+
continue
79+
elif qualified_class_name == 'builtin_interfaces.msg._time.Time' and \
80+
field_value == 'now' and expand_time_now:
81+
timestamp_fields.append(partial(setattr, msg, field_name))
82+
continue
83+
else:
84+
try:
85+
value = field_type(field_value)
86+
except TypeError:
87+
value = field_type()
88+
set_message_fields_internal(
89+
value, field_value, timestamp_fields)
90+
rosidl_type = get_message_slot_types(msg)[field_name]
91+
# Check if field is an array of ROS messages
92+
if isinstance(rosidl_type, AbstractNestedType):
93+
if isinstance(rosidl_type.value_type, NamespacedType):
94+
field_elem_type = import_message_from_namespaced_type(rosidl_type.value_type)
95+
for n in range(len(value)):
96+
submsg = field_elem_type()
97+
set_message_fields_internal(
98+
submsg, value[n], timestamp_fields)
99+
value[n] = submsg
100+
setattr(msg, field_name, value)
101+
set_message_fields_internal(msg, values, timestamp_fields)
102+
return timestamp_fields

0 commit comments

Comments
 (0)