Skip to content

Commit

Permalink
add support to parse actions from idl files
Browse files Browse the repository at this point in the history
  • Loading branch information
dirk-thomas committed Nov 19, 2018
1 parent 0c718e5 commit 0119915
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 2 deletions.
44 changes: 44 additions & 0 deletions rosidl_parser/rosidl_parser/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
SERVICE_REQUEST_MESSAGE_SUFFIX = '_Request'
SERVICE_RESPONSE_MESSAGE_SUFFIX = '_Response'

ACTION_GOAL_SERVICE_SUFFIX = '_Goal'
ACTION_RESULT_SERVICE_SUFFIX = '_Result'
ACTION_FEEDBACK_MESSAGE_SUFFIX = '_Feedback'


class AbstractType:
"""The base class for all types."""
Expand Down Expand Up @@ -444,6 +448,46 @@ def __init__(self, type_, request, response):
self.response_message = response


class Action:
"""A namespaced type containing the user specific parts if an action."""

__slots__ = (
'structure_type', 'goal_request_message', 'result_response_message',
'feedback_message')

def __init__(self, type_, request, response, feedback):
"""
Constructor.
:param NamespacedType type_: the namespaced type identifying the action
:param Message request: the goal request message
:param Message response: the result response message
:param Message feedback: the feedback message
"""
super().__init__()

assert isinstance(type_, NamespacedType)
self.structure_type = type_

assert isinstance(request, Message)
assert request.structure.type.namespaces == type_.namespaces
assert request.structure.type.name == type_.name + \
ACTION_GOAL_SERVICE_SUFFIX + SERVICE_REQUEST_MESSAGE_SUFFIX
self.goal_request_message = request

assert isinstance(response, Message)
assert response.structure.type.namespaces == type_.namespaces
assert response.structure.type.name == type_.name + \
ACTION_RESULT_SERVICE_SUFFIX + SERVICE_RESPONSE_MESSAGE_SUFFIX
self.result_response_message = response

assert isinstance(feedback, Message)
assert feedback.structure.type.namespaces == type_.namespaces
assert feedback.structure.type.name == type_.name + \
ACTION_FEEDBACK_MESSAGE_SUFFIX
self.feedback_message = feedback


class IdlLocator:
"""A URL of an IDL file."""

Expand Down
56 changes: 54 additions & 2 deletions rosidl_parser/rosidl_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
from lark.tree import Tree

from rosidl_parser.definition import AbstractType
from rosidl_parser.definition import Action
from rosidl_parser.definition import ACTION_FEEDBACK_MESSAGE_SUFFIX
from rosidl_parser.definition import ACTION_GOAL_SERVICE_SUFFIX
from rosidl_parser.definition import ACTION_RESULT_SERVICE_SUFFIX
from rosidl_parser.definition import Annotation
from rosidl_parser.definition import Array
from rosidl_parser.definition import BasicType
Expand Down Expand Up @@ -161,10 +165,58 @@ def extract_content_from_ast(tree):
request, response)
content.elements.append(srv)

elif len(struct_defs) == 3:
goal_request = Message(Structure(NamespacedType(
namespaces=get_module_identifier_values(tree, struct_defs[0]),
name=get_first_identifier_value(struct_defs[0]))))
assert goal_request.structure.type.name.endswith(
ACTION_GOAL_SERVICE_SUFFIX + SERVICE_REQUEST_MESSAGE_SUFFIX)
add_message_members(goal_request, struct_defs[0])
resolve_typedefed_names(goal_request.structure, typedefs)
# TODO move "global" constants/enums within a "matching" namespace into
# the goal request message

result_response = Message(Structure(NamespacedType(
namespaces=get_module_identifier_values(tree, struct_defs[1]),
name=get_first_identifier_value(struct_defs[1]))))
assert result_response.structure.type.name.endswith(
ACTION_RESULT_SERVICE_SUFFIX + SERVICE_RESPONSE_MESSAGE_SUFFIX)
add_message_members(result_response, struct_defs[1])
resolve_typedefed_names(result_response.structure, typedefs)
# TODO move "global" constants/enums within a "matching" namespace into
# the result response message

assert goal_request.structure.type.namespaces == \
result_response.structure.type.namespaces
goal_request_basename = goal_request.structure.type.name[
:-len(ACTION_GOAL_SERVICE_SUFFIX +
SERVICE_REQUEST_MESSAGE_SUFFIX)]
result_response_basename = result_response.structure.type.name[
:-len(ACTION_RESULT_SERVICE_SUFFIX +
SERVICE_RESPONSE_MESSAGE_SUFFIX)]
assert goal_request_basename == result_response_basename

feedback_message = Message(Structure(NamespacedType(
namespaces=get_module_identifier_values(tree, struct_defs[2]),
name=get_first_identifier_value(struct_defs[2]))))
assert feedback_message.structure.type.name.endswith(
ACTION_FEEDBACK_MESSAGE_SUFFIX)
add_message_members(feedback_message, struct_defs[2])
resolve_typedefed_names(feedback_message.structure, typedefs)
# TODO move "global" constants/enums within a "matching" namespace into
# the feedback message

action = Action(
NamespacedType(
namespaces=goal_request.structure.type.namespaces,
name=goal_request_basename),
goal_request, result_response, feedback_message)
content.elements.append(action)

else:
assert False, \
'Currently only .idl files with 1 (a message) or 2 (a service) ' \
'structures are supported'
'Currently only .idl files with 1 (a message), 2 (a service) ' \
'and 3 (an action) structures are supported'

return content

Expand Down
14 changes: 14 additions & 0 deletions rosidl_parser/test/action/MyAction.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module rosidl_parser {
module action {
struct MyAction_Goal_Request {
short short_value;
string string_value;
};
struct MyAction_Result_Response {
boolean boolean_value;
};
struct MyAction_Feedback {
float progress_value;
};
};
};
21 changes: 21 additions & 0 deletions rosidl_parser/test/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import pytest

from rosidl_parser.definition import Action
from rosidl_parser.definition import Array
from rosidl_parser.definition import BasicType
from rosidl_parser.definition import BoundedSequence
Expand All @@ -33,6 +34,8 @@
pathlib.Path(__file__).parent, pathlib.Path('msg') / 'MyMessage.idl')
SERVICE_IDL_LOCATOR = IdlLocator(
pathlib.Path(__file__).parent, pathlib.Path('srv') / 'MyService.idl')
ACTION_IDL_LOCATOR = IdlLocator(
pathlib.Path(__file__).parent, pathlib.Path('action') / 'MyAction.idl')


@pytest.fixture(scope='module')
Expand Down Expand Up @@ -180,3 +183,21 @@ def test_service_parser(service_idl_file):
assert srv.structure_type.name == 'MyService'
assert len(srv.request_message.structure.members) == 2
assert len(srv.response_message.structure.members) == 1


@pytest.fixture(scope='module')
def action_idl_file():
return parse_idl_file(ACTION_IDL_LOCATOR)


def test_action_parser(action_idl_file):
actions = action_idl_file.content.get_elements_of_type(Action)
assert len(actions) == 1

action = actions[0]
assert isinstance(action, Action)
assert action.structure_type.namespaces == ['rosidl_parser', 'action']
assert action.structure_type.name == 'MyAction'
assert len(action.goal_request_message.structure.members) == 2
assert len(action.result_response_message.structure.members) == 1
assert len(action.feedback_message.structure.members) == 1

0 comments on commit 0119915

Please sign in to comment.