ros2 icon indicating copy to clipboard operation
ros2 copied to clipboard

Getting state of LifecycleNode

Open mcmindcoder opened this issue 7 years ago • 3 comments

I was trying to use ROS2 LifecycleNode as explained here: http://design.ros2.org/articles/node_lifecycle.html https://github.com/ros2/ros2/wiki/Managed-Nodes

I did some experiments and had a number issues, while trying to make it work. I have ROS2 running on Ubuntu and a LifecycleNode in one thread. I want to request the state of that LifecycleNode from another thread.
Here is a code for that request: https://gist.github.com/mc-android-developer/fda2634f1553d789c974237bde8db0b3

When I calling getState() function, it only works on certain conditions. Here are some working samples:

    // Case#1 Works

    auto task = std::async(std::launch::async, [&node] {
        std::this_thread::sleep_for(1s);
        auto state = getState();
    });

    rclcpp::spin(node);
    // Case#2 Works

    auto task = std::async(std::launch::async, [&node] {
        std::this_thread::sleep_for(1s);
        auto state = getState();
    });

    auto timer = node->create_wall_timer(1s, [&node]() {
        std::cout 
    // Case#3 Works

    rclcpp::executors::SingleThreadedExecutor controllerExecutor;

    controllerExecutor.add_node(node);
    auto spinLambda= [&controllerExecutor]() {
        controllerExecutor.spin();
    };

    std::thread nodeExecutionThread(spinLambda);

    auto state = getState();

    nodeExecutionThread.join();

and some NOT working samples:

    // Case#9 NOT Works

    auto timer = node->create_wall_timer(1s, [&node]() {
            std::this_thread::sleep_for(1s);
            auto state = getState();
    });

    rclcpp::spin(node);

    // Case#10 NOT Works

    auto timer = node->create_wall_timer(1s, [&node]() {
        std::async(std::launch::async, [&node] {
            std::this_thread::sleep_for(1s);
            auto state = getState();
        });
    });

    rclcpp::spin(node);
    // Case#11 NOT Works

    rclcpp::executors::SingleThreadedExecutor controllerExecutor;

    controllerExecutor.add_node(node);
    auto spinLambda= [&controllerExecutor]() {
        controllerExecutor.spin();
    };

    auto timer = node->create_wall_timer(3s, [&node]() {
        auto state = getState();
    });

    std::thread nodeExecutionThread(spinLambda);
    nodeExecutionThread.join();
    // Case#12 NOT Works
    
    rclcpp::executors::SingleThreadedExecutor controllerExecutor;

    controllerExecutor.add_node(node);
    auto spinLambda= [&controllerExecutor]() {
        controllerExecutor.spin();
    };

    auto timer = node->create_wall_timer(1s, [&node]() {
        std::async(std::launch::async, [&node] {
            std::this_thread::sleep_for(1s);
            auto state = getState();
        });
    });

    std::thread nodeExecutionThread(spinLambda);
    nodeExecutionThread.join();

'NOT Works' mean that getState() function comes to line // EXIT BY TIMEOUT

Could you help me to understand why cases 9-16 do not work? Is ROS2 designed to work this way OR it's an issue?

mcmindcoder avatar May 03 '17 00:05 mcmindcoder

thanks for reporting such a verbose feedback. I haven't had time to look at every case in detail, but what I believe is happening (at least for case 9-10) is that you essentially try to call spin within one spin execution. That is, your timer callback gets executed within your first round of spin and you wait for your service within this callback. However, this service would need yet another call to spin in order to get executed. For this second round of spin, your timer callback has to return first. You experience this behavior your first cases where your spin and wait_for_service always run in two separate threads.

Does this make sense what I am saying here?

I will look into the other cases in more detail once I have same spare cycles.

Karsten1987 avatar May 03 '17 01:05 Karsten1987

Thank you for the prompt response.

Yes, it makes sense for case#9, cos I request state in the same thread where spinning happens. In case#10 however, new thread is created inside the spinning thread and state is requested asynchronously from that new thread. The spinning thread is not delayed this way, isn't it?

mcmindcoder avatar May 03 '17 01:05 mcmindcoder

I guess I fixed it for case#10. It should be something like:

 auto timer = node->create_wall_timer(1s, [&node]() {
        static auto task = std::async(std::launch::async, [&node] {
            std::this_thread::sleep_for(1s);
            auto state = getState();
        });
    });

It works this way.

mcmindcoder avatar May 03 '17 20:05 mcmindcoder