Parse msg bounded fields and default values
This commit is contained in:
parent
ae13edd221
commit
aaa9969856
@ -9,6 +9,10 @@ Message instances
|
|||||||
-----------------
|
-----------------
|
||||||
The type system generates a dataclass for each message type. These dataclasses give direct read write access to all mutable fields of a message. Fields should be mutated with care as no type checking is applied during runtime.
|
The type system generates a dataclass for each message type. These dataclasses give direct read write access to all mutable fields of a message. Fields should be mutated with care as no type checking is applied during runtime.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Limitation: While the type system parses message definitions with array bounds and/or default values, neither bounds nor default values are enforced or assigned to message instances.
|
||||||
|
|
||||||
Extending the type system
|
Extending the type system
|
||||||
-------------------------
|
-------------------------
|
||||||
Adding custom message types consists of two steps. First, message definitions are converted into parse trees using :py:func:`get_types_from_idl() <rosbags.typesys.get_types_from_idl>` or :py:func:`get_types_from_msg() <rosbags.typesys.get_types_from_msg>`, and second the types are registered in the type system via :py:func:`register_types() <rosbags.typesys.register_types>`. The following example shows how to add messages type definitions from ``.msg`` and ``.idl`` files:
|
Adding custom message types consists of two steps. First, message definitions are converted into parse trees using :py:func:`get_types_from_idl() <rosbags.typesys.get_types_from_idl>` or :py:func:`get_types_from_msg() <rosbags.typesys.get_types_from_msg>`, and second the types are registered in the type system via :py:func:`register_types() <rosbags.typesys.register_types>`. The following example shows how to add messages type definitions from ``.msg`` and ``.idl`` files:
|
||||||
|
|||||||
@ -48,24 +48,28 @@ const_dcl
|
|||||||
/ type_spec identifier '=' integer_literal
|
/ type_spec identifier '=' integer_literal
|
||||||
|
|
||||||
field_dcl
|
field_dcl
|
||||||
= type_spec identifier
|
= type_spec identifier default_value?
|
||||||
|
|
||||||
type_spec
|
type_spec
|
||||||
= array_type_spec
|
= array_type_spec
|
||||||
|
/ bounded_array_type_spec
|
||||||
/ simple_type_spec
|
/ simple_type_spec
|
||||||
|
|
||||||
array_type_spec
|
array_type_spec
|
||||||
= simple_type_spec array_size
|
= simple_type_spec array_size
|
||||||
|
|
||||||
|
bounded_array_type_spec
|
||||||
|
= simple_type_spec array_bounds
|
||||||
|
|
||||||
simple_type_spec
|
simple_type_spec
|
||||||
= scoped_name
|
= 'string' '<=' integer_literal
|
||||||
|
/ scoped_name
|
||||||
|
|
||||||
array_size
|
array_size
|
||||||
= '[' integer_literal? ']'
|
= '[' integer_literal? ']'
|
||||||
|
|
||||||
integer_literal
|
array_bounds
|
||||||
= r'[-+]?[1-9][0-9]+'
|
= '[<=' integer_literal ']'
|
||||||
/ r'[-+]?[0-9]'
|
|
||||||
|
|
||||||
scoped_name
|
scoped_name
|
||||||
= identifier '/' scoped_name
|
= identifier '/' scoped_name
|
||||||
@ -73,6 +77,50 @@ scoped_name
|
|||||||
|
|
||||||
identifier
|
identifier
|
||||||
= r'[a-zA-Z_][a-zA-Z_0-9]*'
|
= r'[a-zA-Z_][a-zA-Z_0-9]*'
|
||||||
|
|
||||||
|
default_value
|
||||||
|
= literal
|
||||||
|
|
||||||
|
literal
|
||||||
|
= boolean_literal
|
||||||
|
/ float_literal
|
||||||
|
/ integer_literal
|
||||||
|
/ string_literal
|
||||||
|
/ array_literal
|
||||||
|
|
||||||
|
boolean_literal
|
||||||
|
= 'true'
|
||||||
|
/ 'false'
|
||||||
|
|
||||||
|
integer_literal
|
||||||
|
= hexadecimal_literal
|
||||||
|
/ octal_literal
|
||||||
|
/ decimal_literal
|
||||||
|
|
||||||
|
decimal_literal
|
||||||
|
= r'[-+]?[1-9][0-9]+'
|
||||||
|
/ r'[-+]?[0-9]'
|
||||||
|
|
||||||
|
octal_literal
|
||||||
|
= r'[-+]?0[0-7]+'
|
||||||
|
|
||||||
|
hexadecimal_literal
|
||||||
|
= r'[-+]?0[xX][a-fA-F0-9]+'
|
||||||
|
|
||||||
|
float_literal
|
||||||
|
= r'[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?'
|
||||||
|
/ r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)'
|
||||||
|
|
||||||
|
string_literal
|
||||||
|
= '"' r'(\\"|[^"])*' '"'
|
||||||
|
/ '\'' r'(\\\'|[^'])*' '\''
|
||||||
|
|
||||||
|
array_literal
|
||||||
|
= '[' array_elements? ']'
|
||||||
|
|
||||||
|
array_elements
|
||||||
|
= literal ',' array_elements
|
||||||
|
/ literal
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@ -207,15 +255,20 @@ class VisitorMSG(Visitor):
|
|||||||
return Nodetype.ARRAY, (children[0], length[0])
|
return Nodetype.ARRAY, (children[0], length[0])
|
||||||
return Nodetype.SEQUENCE, (children[0], None)
|
return Nodetype.SEQUENCE, (children[0], None)
|
||||||
|
|
||||||
|
def visit_bounded_array_type_spec(self, children: Any) -> Any:
|
||||||
|
"""Process bounded array type specifier."""
|
||||||
|
return Nodetype.SEQUENCE, (children[0], None)
|
||||||
|
|
||||||
def visit_simple_type_spec(self, children: Any) -> Any:
|
def visit_simple_type_spec(self, children: Any) -> Any:
|
||||||
"""Process simple type specifier."""
|
"""Process simple type specifier."""
|
||||||
|
typespec = children[0][1] if ('LITERAL', '<=') in children else children[1]
|
||||||
dct = {
|
dct = {
|
||||||
'time': 'builtin_interfaces/msg/Time',
|
'time': 'builtin_interfaces/msg/Time',
|
||||||
'duration': 'builtin_interfaces/msg/Duration',
|
'duration': 'builtin_interfaces/msg/Duration',
|
||||||
'byte': 'uint8',
|
'byte': 'uint8',
|
||||||
'char': 'uint8',
|
'char': 'uint8',
|
||||||
}
|
}
|
||||||
return Nodetype.NAME, dct.get(children[1], children[1])
|
return Nodetype.NAME, dct.get(typespec, typespec)
|
||||||
|
|
||||||
def visit_scoped_name(self, children: Any) -> Any:
|
def visit_scoped_name(self, children: Any) -> Any:
|
||||||
"""Process scoped name."""
|
"""Process scoped name."""
|
||||||
@ -228,10 +281,30 @@ class VisitorMSG(Visitor):
|
|||||||
"""Process identifier."""
|
"""Process identifier."""
|
||||||
return (Nodetype.NAME, children)
|
return (Nodetype.NAME, children)
|
||||||
|
|
||||||
def visit_integer_literal(self, children: Any) -> Any:
|
def visit_boolean_literal(self, children: Any) -> Any:
|
||||||
"""Process integer literal."""
|
"""Process boolean literal."""
|
||||||
|
return children[1] == 'TRUE'
|
||||||
|
|
||||||
|
def visit_float_literal(self, children: Any) -> Any:
|
||||||
|
"""Process float literal."""
|
||||||
|
return float(children)
|
||||||
|
|
||||||
|
def visit_decimal_literal(self, children: Any) -> Any:
|
||||||
|
"""Process decimal integer literal."""
|
||||||
return int(children)
|
return int(children)
|
||||||
|
|
||||||
|
def visit_octal_literal(self, children: Any) -> Any:
|
||||||
|
"""Process octal integer literal."""
|
||||||
|
return int(children, 8)
|
||||||
|
|
||||||
|
def visit_hexadecimal_literal(self, children: Any) -> Any:
|
||||||
|
"""Process hexadecimal integer literal."""
|
||||||
|
return int(children, 16)
|
||||||
|
|
||||||
|
def visit_string_literal(self, children: Any) -> Any:
|
||||||
|
"""Process integer literal."""
|
||||||
|
return children[1]
|
||||||
|
|
||||||
|
|
||||||
def get_types_from_msg(text: str, name: str) -> Typesdict:
|
def get_types_from_msg(text: str, name: str) -> Typesdict:
|
||||||
"""Get type from msg message definition.
|
"""Get type from msg message definition.
|
||||||
|
|||||||
@ -29,6 +29,30 @@ float64[] seq2
|
|||||||
float64[4] array
|
float64[4] array
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
MSG_BOUNDS = """
|
||||||
|
int32[] unbounded_integer_array
|
||||||
|
int32[5] five_integers_array
|
||||||
|
int32[<=5] up_to_five_integers_array
|
||||||
|
|
||||||
|
string string_of_unbounded_size
|
||||||
|
string<=10 up_to_ten_characters_string
|
||||||
|
|
||||||
|
string[<=5] up_to_five_unbounded_strings
|
||||||
|
string<=10[] unbounded_array_of_string_up_to_ten_characters_each
|
||||||
|
string<=10[<=5] up_to_five_strings_up_to_ten_characters_each
|
||||||
|
"""
|
||||||
|
|
||||||
|
MSG_DEFAULTS = """
|
||||||
|
bool b false
|
||||||
|
uint8 i 42
|
||||||
|
uint8 o 0377
|
||||||
|
uint8 h 0xff
|
||||||
|
float32 y -314.15e-2
|
||||||
|
string name1 "John"
|
||||||
|
string name2 'Ringo'
|
||||||
|
int32[] samples [-200, -100, 0, 100, 200]
|
||||||
|
"""
|
||||||
|
|
||||||
MULTI_MSG = """
|
MULTI_MSG = """
|
||||||
std_msgs/Header header
|
std_msgs/Header header
|
||||||
byte b
|
byte b
|
||||||
@ -124,6 +148,46 @@ def test_parse_empty_msg():
|
|||||||
assert ret == {'std_msgs/msg/Empty': ([], [])}
|
assert ret == {'std_msgs/msg/Empty': ([], [])}
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_bounds_msg():
|
||||||
|
"""Test msg parser."""
|
||||||
|
ret = get_types_from_msg(MSG_BOUNDS, 'test_msgs/msg/Foo')
|
||||||
|
assert ret == {
|
||||||
|
'test_msgs/msg/Foo': (
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
('unbounded_integer_array', (4, ((1, 'int32'), None))),
|
||||||
|
('five_integers_array', (3, ((1, 'int32'), 5))),
|
||||||
|
('up_to_five_integers_array', (4, ((1, 'int32'), None))),
|
||||||
|
('string_of_unbounded_size', (1, 'string')),
|
||||||
|
('up_to_ten_characters_string', (1, 'string')),
|
||||||
|
('up_to_five_unbounded_strings', (4, ((1, 'string'), None))),
|
||||||
|
('unbounded_array_of_string_up_to_ten_characters_each', (4, ((1, 'string'), None))),
|
||||||
|
('up_to_five_strings_up_to_ten_characters_each', (4, ((1, 'string'), None))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_defaults_msg():
|
||||||
|
"""Test msg parser."""
|
||||||
|
ret = get_types_from_msg(MSG_DEFAULTS, 'test_msgs/msg/Foo')
|
||||||
|
assert ret == {
|
||||||
|
'test_msgs/msg/Foo': (
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
('b', (1, 'bool')),
|
||||||
|
('i', (1, 'uint8')),
|
||||||
|
('o', (1, 'uint8')),
|
||||||
|
('h', (1, 'uint8')),
|
||||||
|
('y', (1, 'float32')),
|
||||||
|
('name1', (1, 'string')),
|
||||||
|
('name2', (1, 'string')),
|
||||||
|
('samples', (4, ((1, 'int32'), None))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_parse_msg():
|
def test_parse_msg():
|
||||||
"""Test msg parser."""
|
"""Test msg parser."""
|
||||||
with pytest.raises(TypesysError, match='Could not parse'):
|
with pytest.raises(TypesysError, match='Could not parse'):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user