Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate msg/srv from within an 'action' directory/module #15

Merged

Conversation

apojomovsky
Copy link
Contributor

@apojomovsky apojomovsky commented Oct 26, 2018

Closes #13 .
This PR allows the generation of messages and services that are defined in the module action.
It depends on ros2/rosidl#308 , ros2/rosidl_dds#44 and ros2/rosidl_typesupport#33 to compile successfully.
It was tested with the definitions found in this rcl_interfaces' branch .

connects to ros2/rosidl#301

@tfoote tfoote added the in progress Actively being worked on (Kanban column) label Oct 26, 2018
Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apojomovsky I left a few comments !

@@ -105,15 +105,15 @@ const rosidl_message_type_support_t *
ROSIDL_GET_MSG_TYPE_SUPPORT(@(pkg_name), @(subfolder), @(spec.msg_name));

int8_t
_register_msg_type__@(type_name)(PyObject * pymodule)
_register_msg_type__@(type_name)__@(subfolder)(PyObject * pymodule)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apojomovsky consider reversing the order of interpolated variables, as in _register_msg_type__@(subfolder)_@(type_name)(PyObject * pymodule).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're right! Thanks for the suggestion, done!


int8_t
_register_srv_type__@(type_name)(PyObject * pymodule)
_register_srv_type__@(type_name)__@(subfolder)(PyObject * pymodule)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apojomovsky same as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, thanks!

@[ if spec.base_type.pkg_name != field.type.pkg_name]@
ROSIDL_GENERATOR_C_IMPORT
@[ end if]@
PyObject * @(field.type.pkg_name)__@(lowercase_field_type)__convert_to_py(void * raw_ros_message);
PyObject * @(field.type.pkg_name)__@(subfolder)__@(lowercase_field_type)__convert_to_py(void * raw_ros_message);
Copy link
Contributor

@hidmic hidmic Oct 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apojomovsky I don't think this will be true in general e.g. a _Request message that is part of a service definition below srv/ could have a field that's a message described in a message definition below msg/.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I just restored this line. Thanks!

@apojomovsky
Copy link
Contributor Author

Thanks for the review @hidmic , I just finished addressing your comments. Could you PTAL?

@@ -65,6 +67,15 @@ foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES})
list(APPEND _generated_srv_py_files
"${_output_path}/${_parent_folder}/_${_module_name}.py"
)
elseif(_parent_folder STREQUAL "action")
if("_${_module_name}_s.c" MATCHES "(.*)__response(.*)" OR "_${_module_name}_s.c" MATCHES "(.*)__request(.*)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for services, but not messages. After adding action/Something.msg in test_msgs I see this traceback

undefined symbol: test_msgs__action__something__convert_from_py

342: (test_publisher) pid 7968: ['/usr/bin/python3', '/home/developer/workspaces/ros2-dev0/src/ros2/system_tests/test_communication/test/publisher_py.py', 'BoundedArrayPrimitives', '/test_time_23_08_40'] (stderr > stdout, all > console)
342: (test_subscriber) pid 7969: ['/home/developer/workspaces/ros2-dev0/build/test_communication/test_subscriber_cpp', 'BoundedArrayPrimitives', '/test_time_23_08_40'] (stderr > stdout, all > console)
342: [test_publisher] exception in talker:
342: [test_publisher] Traceback (most recent call last):
342: [test_publisher]   File "/home/developer/workspaces/ros2-dev0/install/rosidl_generator_py/lib/python3.6/site-packages/rosidl_generator_py/import_type_support_impl.py", line 42, in import_type_support
342: [test_publisher]     return importlib.import_module(module_name, package=pkg_name)
342: [test_publisher]   File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
342: [test_publisher]     return _bootstrap._gcd_import(name[level:], package, level)
342: [test_publisher]   File "<frozen importlib._bootstrap>", line 994, in _gcd_import
342: [test_publisher]   File "<frozen importlib._bootstrap>", line 971, in _find_and_load
342: [test_publisher]   File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
342: [test_publisher]   File "<frozen importlib._bootstrap>", line 658, in _load_unlocked
342: [test_publisher]   File "<frozen importlib._bootstrap>", line 571, in module_from_spec
342: [test_publisher]   File "<frozen importlib._bootstrap_external>", line 922, in create_module
342: [test_publisher]   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
342: [test_publisher] ImportError: /home/developer/workspaces/ros2-dev0/install/test_msgs/lib/python3.6/site-packages/test_msgs/test_msgs_s__rosidl_typesupport_c.cpython-36m-x86_64-linux-gnu.so: undefined symbol: test_msgs__action__something__convert_from_py

Copy link
Contributor

@sloretz sloretz Oct 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing this condition gets a little further,

    get_filename_component(_ext "${_idl_file}" EXT)
    # C files generated for <msg>.msg, <service>_Request.msg and <service>_Response.msg but not <service>.srv
    if(_ext STREQUAL ".msg")
      list(APPEND _generated_action_c_files
        "${_output_path}/${_parent_folder}/_${_module_name}_s.c"
      )
    endif()
    list(APPEND _generated_action_py_files
      "${_output_path}/${_parent_folder}/_${_module_name}.py"
    )

Edit: NVM, the test communication problem is not caused by this PR
Though test_communication still fails with

18: [test_publisher] exception in talker:
18: [test_publisher] Traceback (most recent call last):
18: [test_publisher]   File "/home/developer/workspaces/ros2-dev0/src/ros2/system_tests/test_communication/test/publisher_py.py", line 63, in <module>
18: [test_publisher]     number_of_cycles=args.number_of_cycles)
18: [test_publisher]   File "/home/developer/workspaces/ros2-dev0/src/ros2/system_tests/test_communication/test/publisher_py.py", line 27, in talker
18: [test_publisher]     msg_mod = getattr(module, message_name)
18: [test_publisher] AttributeError: module 'test_msgs.msg' has no attribute 'Something'
18: [test_subscriber] exception in subscriber:
18: [test_subscriber] Traceback (most recent call last):
18: [test_subscriber]   File "/home/developer/workspaces/ros2-dev0/src/ros2/system_tests/test_communication/test/subscriber_py.py", line 81, in <module>
18: [test_subscriber]     listener(message_name=args.message_name, namespace=args.namespace)
18: [test_subscriber]   File "/home/developer/workspaces/ros2-dev0/src/ros2/system_tests/test_communication/test/subscriber_py.py", line 47, in listener
18: [test_subscriber]     msg_mod = getattr(module, message_name)
18: [test_subscriber] AttributeError: module 'test_msgs.msg' has no attribute 'Something'

but I'm not yet sure if the problem is here or there

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 98d630c

Copy link
Contributor

@sloretz sloretz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested with

Repos file
repositories:
  ros2/rcl_interfaces:
    type: git
    url: https://github.com/ros2/rcl_interfaces.git
    version: sloretz/msg_srv_in_action
  ros2/rosidl:
    type: git
    url: https://github.com/ros2/rosidl.git
    version: hidmic/prepare_for_action_generation
  ros2/rosidl_dds:
    type: git
    url: https://github.com/ros2/rosidl_dds.git
    version: hidmic/prepare_for_action_generation
  ros2/rosidl_python:
    type: git
    url: https://github.com/ros2/rosidl_python.git
    version: apojomovsky/allow_generating_msg_and_srv_in_action
  ros2/rosidl_typesupport:
    type: git
    url: https://github.com/ros2/rosidl_typesupport.git
    version: hidmic/prepare_for_action_generation
  ros2/rosidl_typesupport_connext:
    type: git
    url: https://github.com/ros2/rosidl_typesupport_connext.git
    version: hidmic/subfolder_aware_srv_generation
  ros2/rosidl_typesupport_fastrtps:
    type: git
    url: https://github.com/ros2/rosidl_typesupport_fastrtps.git
    version: apojomovsky/subfolder_aware_srv_generation
  ros2/rosidl_typesupport_opensplice:
    type: git
    url: https://github.com/ros2/rosidl_typesupport_opensplice.git
    version: sservulo/prepare_for_action_generation
  ros2/system_tests:
    type: git
    url: https://github.com/ros2/system_tests.git
    version: hidmic/namespace-messages-with-subfolder
Publisher
from time import sleep

import rclpy

from test_msgs.action import Something

# We do not recommend this style as ROS 2 provides timers for this purpose,
# and it is recommended that all nodes call a variation of spin.
# This example is only included for completeness because it is similar to examples in ROS 1.
# For periodic publication please see the other examples using timers.


def main(args=None):
    rclpy.init(args=args)

    node = rclpy.create_node('minimal_publisher')

    publisher = node.create_publisher(Something, 'topic')

    msg = Something()

    i = 0
    while rclpy.ok():
        msg.string_value = 'Hello World: %d' % i
        i += 1
        node.get_logger().info('Publishing: "%s"' % msg.string_value)
        publisher.publish(msg)
        sleep(0.5)  # seconds

    # Destroy the node explicitly
    # (optional - otherwise it will be done automatically
    # when the garbage collector destroys the node object)
    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()
Subscriber
import rclpy

from test_msgs.action import Something

g_node = None


def chatter_callback(msg):
    global g_node
    g_node.get_logger().info(
        'I heard: "%s"' % msg.string_value)


def main(args=None):
    global g_node
    rclpy.init(args=args)

    g_node = rclpy.create_node('minimal_subscriber')

    subscription = g_node.create_subscription(Something, 'topic', chatter_callback)
    subscription  # prevent unused variable warning

    while rclpy.ok():
        rclpy.spin_once(g_node)

    # Destroy the node explicitly
    # (optional - otherwise it will be done automatically
    # when the garbage collector destroys the node object)
    g_node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()
Service Client
from test_msgs.action import Primitives

import rclpy


def main(args=None):
    rclpy.init(args=args)
    node = rclpy.create_node('minimal_client')
    cli = node.create_client(Primitives, 'add_two_ints')

    req = Primitives.Request()
    req.int8_value = 41
    req.int16_value = 1
    while not cli.wait_for_service(timeout_sec=1.0):
        node.get_logger().info('service not available, waiting again...')

    future = cli.call_async(req)
    rclpy.spin_until_future_complete(node, future)

    if future.result() is not None:
        node.get_logger().info(
            'Result of add_two_ints: for %d + %d = %d' %
            (req.int8_value, req.int16_value, future.result().int32_value))
    else:
        node.get_logger().info('Service call failed %r' % (future.exception(),))

    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()
Service server
from test_msgs.action import Primitives

import rclpy

g_node = None


def add_two_ints_callback(request, response):
    global g_node
    response.int32_value = request.int8_value + request.int16_value
    g_node.get_logger().info(
        'Incoming request\na: %d b: %d' % (request.int8_value, request.int16_value))

    return response


def main(args=None):
    global g_node
    rclpy.init(args=args)

    g_node = rclpy.create_node('minimal_service')

    srv = g_node.create_service(Primitives, 'add_two_ints', add_two_ints_callback)
    while rclpy.ok():
        rclpy.spin_once(g_node)

    # Destroy the service attached to the node explicitly
    # (optional - otherwise it will be done automatically
    # when the garbage collector destroys the node object)
    g_node.destroy_service(srv)
    rclpy.shutdown()


if __name__ == '__main__':
    main()

@hidmic
Copy link
Contributor

hidmic commented Oct 30, 2018

@sloretz cbf6a2c fixes an issue with Python bindings for messages with the same name under different subfolders.

@apojomovsky
Copy link
Contributor Author

Ok, given that we have green light I'm proceeding to merge this and its related PRs

@apojomovsky apojomovsky merged commit 0df76f5 into master Oct 30, 2018
@tfoote tfoote removed the in progress Actively being worked on (Kanban column) label Oct 30, 2018
@apojomovsky apojomovsky deleted the apojomovsky/allow_generating_msg_and_srv_in_action branch October 30, 2018 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants