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

Action server example #1129

Closed
metanav opened this issue Aug 10, 2022 · 29 comments
Closed

Action server example #1129

metanav opened this issue Aug 10, 2022 · 29 comments

Comments

@metanav
Copy link

metanav commented Aug 10, 2022

  • Hardware description: Arduino Nano RP2040 Connect
  • RTOS: Mbed OS
  • Installation type: Arduino Library
  • Version: humble

There is no action server example.
I would like to implement an action server to drive actuator using MoveIt2 JointTrajectory type request.

@pablogs9
Copy link
Member

Feel free to contribute with a PR

@metanav
Copy link
Author

metanav commented Aug 10, 2022

Hi @pablogs9,

I have created an example (see at the end) and running it on Arduino Nicla Vision which have same processor as Arduino Portenta. I am not using Arduino Nano RP2040 connect for action server since the prebuild using colcon_verylowmem.meta and RMW_UXRCE_MAX_SERVICES is set to 0. I guess the Arduino Nicla Vision uses the colcon.meta as I can confirm that micro-ros_addtwoints_service example ran successfully and I was able to interact with the service. I am not sure if "-DRMW_UXRCE_MAX_SERVICES=1" is still applicable to the action server or we need something else to enable it.

After running the action service example code, micro-ros-agent prints the log message (see below). But I am not able to see the action name in theros2 action listcommand output.


[1660112425.966313] info     | TermiosAgentLinux.cpp | init                     | running...             | fd: 3
[1660112426.333040] info     | Root.cpp           | create_client            | create                 | client_key: 0x2D493D99, session_id: 0x81
[1660112426.333172] info     | SessionManager.hpp | establish_session        | session established    | client_key: 0x2D493D99, address: 0
[1660112426.348249] info     | ProxyClient.cpp    | create_participant       | participant created    | client_key: 0x2D493D99, participant_id: 0x000(1)
[1660112426.350275] info     | ProxyClient.cpp    | create_replier           | replier created        | client_key: 0x2D493D99, requester_id: 0x000(7), participant_id: 0x000(1)


Example Action Server Sketch:

#include <mbed.h>
#include <micro_ros_arduino.h>

#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>

#include <example_interfaces/action/fibonacci.h>

using namespace mbed;
using namespace rtos;

#define LED_PIN 13
#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){error_loop();}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}


const char * goalResult[7] = { "", "", "", "", "succeeded", "canceled", "aborted"};

rclc_executor_t executor;
Thread th;
//char buff[100];

void error_loop()
{
  for (;;)
  {
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    delay(100);
  }
}


void fibonacci_worker(void * args)
{
  (void) args;
  rclc_action_goal_handle_t * goal_handle = (rclc_action_goal_handle_t *) args;
  rcl_action_goal_state_t goal_state;

  example_interfaces__action__Fibonacci_SendGoal_Request * req =
    (example_interfaces__action__Fibonacci_SendGoal_Request *) goal_handle->ros_goal_request;

  example_interfaces__action__Fibonacci_GetResult_Response response = {0};
  example_interfaces__action__Fibonacci_FeedbackMessage feedback;

  if (req->goal.order < 2) {
    goal_state = GOAL_STATE_ABORTED;
  } else {
    feedback.feedback.sequence.capacity = req->goal.order;
    feedback.feedback.sequence.size = 0;
    feedback.feedback.sequence.data =
      (int32_t *) malloc(feedback.feedback.sequence.capacity * sizeof(int32_t));

    feedback.feedback.sequence.data[0] = 0;
    feedback.feedback.sequence.data[1] = 1;
    feedback.feedback.sequence.size = 2;

    for (size_t i = 2; i < (size_t) req->goal.order && !goal_handle->goal_cancelled; i++) {
      feedback.feedback.sequence.data[i] = feedback.feedback.sequence.data[i - 1] +
                                           feedback.feedback.sequence.data[i - 2];
      feedback.feedback.sequence.size++;

      //Serial.println("Publishing feedback");
      rclc_action_publish_feedback(goal_handle, &feedback);
      delay(500);
    }

    if (!goal_handle->goal_cancelled) {
      response.result.sequence.capacity = feedback.feedback.sequence.capacity;
      response.result.sequence.size = feedback.feedback.sequence.size;
      response.result.sequence.data = feedback.feedback.sequence.data;
      goal_state = GOAL_STATE_SUCCEEDED;
    } else {
      goal_state = GOAL_STATE_CANCELED;
    }
  }

  //sprintf(buff, "Goal %d %s, sending result array\n", req->goal.order, goalResult[goal_state]);
  //Serial.print(buff);
  
  rcl_ret_t rc;
  do {
    rc = rclc_action_send_result(goal_handle, goal_state, &response);
    delay(1000);
  } while (rc != RCL_RET_OK);

  free(feedback.feedback.sequence.data);
}

rcl_ret_t handle_goal(rclc_action_goal_handle_t * goal_handle, void * context)
{
  (void) context;

  example_interfaces__action__Fibonacci_SendGoal_Request * req =
    (example_interfaces__action__Fibonacci_SendGoal_Request *) goal_handle->ros_goal_request;

  // Too big, rejecting
  if (req->goal.order > 200) {
    //sprintf(buff, "Goal %d rejected\n", req->goal.order);
    //Serial.print(buff);
    return RCL_RET_ACTION_GOAL_REJECTED;
  }

  //sprintf(buff, "Goal %d accepted\n", req->goal.order);
  //Serial.print(buff);
  //  pthread_t * thread_id = malloc(sizeof(pthread_t));
  //  pthread_create(thread_id, NULL, fibonacci_worker, goal_handle);
  th.start(callback(fibonacci_worker, goal_handle));
  //th.start(fibonacci_worker);

  return RCL_RET_ACTION_GOAL_ACCEPTED;
}

bool handle_cancel(rclc_action_goal_handle_t * goal_handle, void * context)
{
  (void) context;
  (void) goal_handle;

  return true;
}

void setup()
{
  set_microros_transports();

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);


  rcl_allocator_t allocator = rcl_get_default_allocator();
  rclc_support_t support;

  // create init_options
  RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));

  // create node
  rcl_node_t node;
  RCCHECK(rclc_node_init_default(&node, "fibonacci_action_server_rcl", "", &support));

  // Create action service
  rclc_action_server_t action_server;
  RCCHECK(
    rclc_action_server_init_default(
      &action_server,
      &node,
      &support,
      ROSIDL_GET_ACTION_TYPE_SUPPORT(example_interfaces, Fibonacci),
      "fibonacci"
    ));

  // Create executor
  RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));

  example_interfaces__action__Fibonacci_SendGoal_Request ros_goal_request[10];

  RCCHECK(
    rclc_executor_add_action_server(
      &executor,
      &action_server,
      10,
      ros_goal_request,
      sizeof(example_interfaces__action__Fibonacci_SendGoal_Request),
      handle_goal,
      handle_cancel,
      (void *) &action_server));
}

void loop()
{
  delay(100);
  RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
}

       

@pablogs9
Copy link
Member

Actions require 3 services server (goal, cancel and result service servers) and 2 publishers (feedback and status).

Your micro-ROS Agent is only creating one service server, you should increase those values in the colcon.meta and rebuild the library.

Could you try this?

@metanav
Copy link
Author

metanav commented Aug 10, 2022

I have changed the colcon.meta to have 3 services as below and rebuilt the library.

        "rmw_microxrcedds": {
            "cmake-args": [
                "-DRMW_UXRCE_MAX_NODES=1",
                "-DRMW_UXRCE_MAX_PUBLISHERS=10",
                "-DRMW_UXRCE_MAX_SUBSCRIPTIONS=5",
                "-DRMW_UXRCE_MAX_SERVICES=3",
                "-DRMW_UXRCE_MAX_CLIENTS=1",
                "-DRMW_UXRCE_MAX_HISTORY=4",
                "-DRMW_UXRCE_TRANSPORT=custom"
            ]
        }

Now I am able to see the action name in the list so I think the action server has started and listed.

$ ros2 action list
/fibonacci

But the command below says no servers.

$ ros2 action info /fibonacci
Action: /fibonacci
Action clients: 0
Action servers: 0

I tried to send goal but it failed. Is the command line correct?

$ ros2 action send_goal /fibonacci example_interfaces/action/Fibonacci  {order:10}
Failed to populate message fields: AttributeError("'Fibonacci_Goal' object has no attribute 'order:10'")

The micro-ros-agent log:



[1660121865.605716] info     | TermiosAgentLinux.cpp | init                     | running...             | fd: 3
[1660121865.605943] info     | Root.cpp           | set_verbose_level        | logger setup           | verbose_level: 4

[1660121865.728725] info     | Root.cpp           | create_client            | create                 | client_key: 0x2DF25258, session_id: 0x81
[1660121865.728806] info     | SessionManager.hpp | establish_session        | session established    | client_key: 0x2DF25258, address: 0
[1660121865.739347] info     | ProxyClient.cpp    | create_participant       | participant created    | client_key: 0x2DF25258, participant_id: 0x000(1)
[1660121865.740652] info     | ProxyClient.cpp    | create_replier           | replier created        | client_key: 0x2DF25258, requester_id: 0x000(7), participant_id: 0x000(1)
[1660121865.741800] info     | ProxyClient.cpp    | create_replier           | replier created        | client_key: 0x2DF25258, requester_id: 0x001(7), participant_id: 0x000(1)
[1660121865.743137] info     | ProxyClient.cpp    | create_replier           | replier created        | client_key: 0x2DF25258, requester_id: 0x002(7), participant_id: 0x000(1)
[1660121865.743771] info     | ProxyClient.cpp    | create_topic             | topic created          | client_key: 0x2DF25258, topic_id: 0x000(2), participant_id: 0x000(1)
[1660121865.744172] info     | ProxyClient.cpp    | create_publisher         | publisher created      | client_key: 0x2DF25258, publisher_id: 0x000(3), participant_id: 0x000(1)
[1660121865.744779] info     | ProxyClient.cpp    | create_datawriter        | datawriter created     | client_key: 0x2DF25258, datawriter_id: 0x000(5), publisher_id: 0x000(3)
[1660121865.745358] info     | ProxyClient.cpp    | create_topic             | topic created          | client_key: 0x2DF25258, topic_id: 0x001(2), participant_id: 0x000(1)
[1660121865.745755] info     | ProxyClient.cpp    | create_publisher         | publisher created      | client_key: 0x2DF25258, publisher_id: 0x001(3), participant_id: 0x000(1)
[1660121865.746503] info     | ProxyClient.cpp    | create_datawriter        | datawriter created     | client_key: 0x2DF25258, datawriter_id: 0x001(5), publisher_id: 0x001(3)



@pablogs9
Copy link
Member

Now I am able to see the action name in the list so I think the action server has started and listed.

$ ros2 action list
/fibonacci

But the command below says no servers.

$ ros2 action info /fibonacci
Action: /fibonacci
Action clients: 0
Action servers: 0

This can be related to graph manager, but is should work in any case.

I tried to send goal but it failed. Is the command line correct?

$ ros2 action send_goal /fibonacci example_interfaces/action/Fibonacci  {order:10}
Failed to populate message fields: AttributeError("'Fibonacci_Goal' object has no attribute 'order:10'")

Everything seems to be correct, be carefull with the espaces: `

ros2 action send_goal /fibonacci example_interfaces/Fibonacci "order: 5"

@metanav
Copy link
Author

metanav commented Aug 10, 2022

Thank @pablogs9 for the correct syntax.

$ ros2 action send_goal /fibonacci example_interfaces/Fibonacci "order: 5" 
Waiting for an action server to become available...
Sending goal:
     order: 5

The terminal shows the result above forever. I guess it should exit after sending goal. How can I print request in the arduino sketch? I tried Serial.println but it hangs.

microROS Agent log:


[1660128784.665697] debug    | Replier.cpp        | read_fn                  | [==>> DDS <<==]        | client_key: 0x00000000, len: 44, data: 
0000: 01 0F 66 CD 58 F7 BC 3A 01 00 00 00 00 00 12 03 00 00 00 00 01 00 00 00 5A BE 9C F4 7B 82 4E C1
0020: AD 5B 42 79 C4 30 5E 76 05 00 00 00
[1660128784.665868] debug    | SerialAgentLinux.cpp | send_message             | [** <<SER>> **]        | client_key: 0x5CBB0BA0, len: 56, data: 
0000: 81 80 0A 00 09 01 30 00 00 0C 00 08 01 0F 66 CD 58 F7 BC 3A 01 00 00 00 00 00 12 03 00 00 00 00
0020: 01 00 00 00 5A BE 9C F4 7B 82 4E C1 AD 5B 42 79 C4 30 5E 76 05 00 00 00
[1660128784.685467] debug    | SerialAgentLinux.cpp | send_message             | [** <<SER>> **]        | client_key: 0x5CBB0BA0, len: 13, data: 
0000: 81 00 00 00 0B 01 05 00 0A 00 0A 00 80
[1660128784.692516] debug    | SerialAgentLinux.cpp | recv_message             | [==>> SER <<==]        | client_key: 0x5CBB0BA0, len: 13, data: 
0000: 81 00 00 00 0A 01 05 00 0B 00 00 00 80
[1660128784.692579] debug    | SerialAgentLinux.cpp | recv_message             | [==>> SER <<==]        | client_key: 0x5CBB0BA0, len: 48, data: 
0000: 81 80 0A 00 07 01 28 00 00 17 00 08 C4 30 5E 76 05 00 00 00 01 00 00 00 5B 42 79 AD 00 00 00 00
0020: 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
[1660128784.692605] debug    | SerialAgentLinux.cpp | recv_message             | [==>> SER <<==]        | client_key: 0x5CBB0BA0, len: 13, data: 
0000: 81 00 00 00 0A 01 05 00 0B 00 00 00 80
[1660128784.692761] debug    | Replier.cpp        | write                    | [** <<DDS>> **]        | client_key: 0x00000000, len: 36, data: 
0000: C4 30 5E 76 05 00 00 00 01 00 00 00 5B 42 79 AD 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00
0020: 00 00 00 00
[1660128784.692915] debug    | SerialAgentLinux.cpp | send_message             | [** <<SER>> **]        | client_key: 0x5CBB0BA0, len: 13, data: 
0000: 81 00 00 00 0A 01 05 00 0B 00 00 00 80

@pablogs9
Copy link
Member

pablogs9 commented Aug 10, 2022

try with ros2 action send_goal /fibonacci example_interfaces/Fibonacci "order: 5" --feedback to check if the Arduino is sending anything

And do not use Serial.println in your skect! It is being used as transport by micro-ROS and it will confuse the agent!

@metanav
Copy link
Author

metanav commented Aug 10, 2022

With the --feedback it still hangs forever after displaying the same output. I did some debugging using the RGB LED switching on with different color. The code execution goes inside this condition for any order value above/below 2.

void fibonacci_worker(void * args)
{
...
...
  if (req->goal.order < 2) {
    goal_state = GOAL_STATE_ABORTED;
    digitalWrite(LEDR, LOW);
  } else {
...

And finally it goes inside this loop and never comes out.

  rcl_ret_t rc;
  do {
    rc = rclc_action_send_result(goal_handle, goal_state, &response);
    delay(1000);
  } while (rc != RCL_RET_OK);

How can I check the req->goal.order value?

@pablogs9
Copy link
Member

pablogs9 commented Aug 10, 2022

I found one problem in your code, all the local variables that you are defining in function setup() will dissapear when this function returns. That means that when you call loop() for example rclc_action_server_t action_server; may have been smashed in the stack.

Please set all variables as global:

rcl_allocator_t allocator;
rclc_support_t support;
rcl_node_t node;
rclc_action_server_t action_server;
example_interfaces__action__Fibonacci_SendGoal_Request ros_goal_request[10];

void setup()
{
  set_microros_transports();

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);


  allocator = rcl_get_default_allocator();

  // create init_options
  RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));

  // create node
  RCCHECK(rclc_node_init_default(&node, "fibonacci_action_server_rcl", "", &support));

  // Create action service
  RCCHECK(
    rclc_action_server_init_default(
      &action_server,
      &node,
      &support,
      ROSIDL_GET_ACTION_TYPE_SUPPORT(example_interfaces, Fibonacci),
      "fibonacci"
    ));

  // Create executor
  RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));

  RCCHECK(
    rclc_executor_add_action_server(
      &executor,
      &action_server,
      10,
      ros_goal_request,
      sizeof(example_interfaces__action__Fibonacci_SendGoal_Request),
      handle_goal,
      handle_cancel,
      (void *) &action_server));
}

void loop()
{
  delay(100);
  RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
}

@pablogs9
Copy link
Member

Also, make sure that you are handling correctly the memory assigned to feedback and response messages. For testing purposes, you can set .data .capacity and .size to 0 or a fixed value in order to check if everything is working properly

@metanav
Copy link
Author

metanav commented Aug 10, 2022

Thanks for the suggestion! I had moved all variable to the global scope except example_interfaces__action__Fibonacci_SendGoal_Request ros_goal_request[10];

Now I can see the response without blocking.

$ ros2 action send_goal /fibonacci example_interfaces/Fibonacci "order: 5" --feedback
Waiting for an action server to become available...
Sending goal:
     order: 5

Goal accepted with ID: 031a30689cf6408ca09cd9c3d3563efa

Feedback:
    sequence:
- 0
- 1
- 1

Feedback:
    sequence:
- 0
- 1
- 1
- 2

Feedback:
    sequence:
- 0
- 1
- 1
- 2
- 3

Result:
    sequence:
- 0
- 1
- 1
- 2
- 3

Goal finished with status: SUCCEEDED

But it always works one time. The next send_goal request hangs again. Maybe I need to check the thread.

@pablogs9
Copy link
Member

Move example_interfaces__action__Fibonacci_SendGoal_Request ros_goal_request[10]; also to the global scope.

@metanav
Copy link
Author

metanav commented Aug 10, 2022

Yes, I've moved before the last post.

@pablogs9
Copy link
Member

BTW, micro-ROS has not enabled the multithreading locks by default in micro_ros_arduino. You should not be calling the micro-ROS APIs from multiple threads simultaneously.

@metanav
Copy link
Author

metanav commented Aug 10, 2022

Do we really need thread? I have copied the example from the https://github.com/micro-ROS/micro-ROS-demos/blob/humble/rclc/fibonacci_action_server/main.c which used thread so I have ported the code accordingly.

@pablogs9
Copy link
Member

It depends on your use case... Here I would try to lock each call to micro-ROS API in fibonacci_worker and also the calls in loop with a mutex or something similar.

@metanav
Copy link
Author

metanav commented Aug 10, 2022

I tried to wrap each API call inside the fibonacci_worker with mutex lock/unlock but it does not help.

Mutex mtx; // Global

void fibonacci_worker(void * args)
{
...
  mtx.lock();
  rclc_action_publish_feedback(goal_handle, &feedback);
  mtx.unlock();
...
  mtx.lock();
  do {
    rc = rclc_action_send_result(goal_handle, goal_state, &response);
    delay(1000);
  } while (rc != RCL_RET_OK);
  mtx.unlock();
...
}

Also, inside the loop:

void loop()
{
  delay(100);
  mtx.lock();
  RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
  mtx.unlock();
}

@pablogs9
Copy link
Member

Try:

  do {
  mtx.lock();
    rc = rclc_action_send_result(goal_handle, goal_state, &response);
  mtx.unlock();
    delay(1000);
  } while (rc != RCL_RET_OK);

Also, try to handle

  example_interfaces__action__Fibonacci_GetResult_Response response = {0};
  example_interfaces__action__Fibonacci_FeedbackMessage feedback;

with static memory instead of malloc and free

@metanav
Copy link
Author

metanav commented Aug 10, 2022

Now I am using static memory of size 5 and only testing order = 5. Still not working.

int32_t data[5] = {0};
 
 if (req->goal.order < 2) {
   goal_state = GOAL_STATE_ABORTED;
   digitalWrite(LEDR, LOW);
 } else {
   feedback.feedback.sequence.capacity = req->goal.order;
   feedback.feedback.sequence.size = 0;
//    feedback.feedback.sequence.data =
//      (int32_t *) malloc(feedback.feedback.sequence.capacity * sizeof(int32_t));
   feedback.feedback.sequence.data = data;

...

//    free(feedback.feedback.sequence.data);

@pablogs9
Copy link
Member

Share your new code with this modifications

@metanav
Copy link
Author

metanav commented Aug 10, 2022

Here you go:

#include <mbed.h>
#include <micro_ros_arduino.h>
#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <example_interfaces/action/fibonacci.h>

using namespace mbed;
using namespace rtos;

#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){error_loop();}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}


const char * goalResult[7] = { "", "", "", "", "succeeded", "canceled", "aborted"};


rclc_executor_t executor;
rcl_allocator_t allocator;
rclc_support_t support;
rcl_node_t node;
rclc_action_server_t action_server;
example_interfaces__action__Fibonacci_SendGoal_Request ros_goal_request[10];
int32_t data[5] = {0};
 
Thread th;
Mutex mtx;

void error_loop() {
  for (;;) {
    digitalWrite(LEDR, !digitalRead(LEDR));
    delay(100);
  }
}


void fibonacci_worker(void * args)
{

  (void) args;
  rclc_action_goal_handle_t * goal_handle = (rclc_action_goal_handle_t *) args;
  rcl_action_goal_state_t goal_state;

  example_interfaces__action__Fibonacci_SendGoal_Request * req =
    (example_interfaces__action__Fibonacci_SendGoal_Request *) goal_handle->ros_goal_request;

  example_interfaces__action__Fibonacci_GetResult_Response response = {0};
  example_interfaces__action__Fibonacci_FeedbackMessage feedback;
 
  
  if (req->goal.order < 2) {
    goal_state = GOAL_STATE_ABORTED;
    digitalWrite(LEDR, LOW);
  } else {
    feedback.feedback.sequence.capacity = req->goal.order;
    feedback.feedback.sequence.size = 0;
//    feedback.feedback.sequence.data =
//      (int32_t *) malloc(feedback.feedback.sequence.capacity * sizeof(int32_t));
    feedback.feedback.sequence.data = data;
    feedback.feedback.sequence.data[0] = 0;
    feedback.feedback.sequence.data[1] = 1;
    feedback.feedback.sequence.size = 2;

    for (size_t i = 2; i < (size_t) req->goal.order && !goal_handle->goal_cancelled; i++) {
      feedback.feedback.sequence.data[i] = feedback.feedback.sequence.data[i - 1] +
                                           feedback.feedback.sequence.data[i - 2];
      feedback.feedback.sequence.size++;
      mtx.lock();
      rclc_action_publish_feedback(goal_handle, &feedback);
      mtx.unlock();
      delay(500);
    }

    if (!goal_handle->goal_cancelled) {
      response.result.sequence.capacity = feedback.feedback.sequence.capacity;
      response.result.sequence.size = feedback.feedback.sequence.size;
      response.result.sequence.data = feedback.feedback.sequence.data;
      goal_state = GOAL_STATE_SUCCEEDED;
      digitalWrite(LEDG, LOW);

    } else {
      goal_state = GOAL_STATE_CANCELED;
      digitalWrite(LEDB, LOW);
    }
  }

  rcl_ret_t rc;
 
  do {
    mtx.lock();
    rc = rclc_action_send_result(goal_handle, goal_state, &response);
    mtx.unlock();
    delay(1000);
  } while (rc != RCL_RET_OK);
  

//  if (goal_state != GOAL_STATE_ABORTED) {
//    free(feedback.feedback.sequence.data);
//  }
// th.terminate();
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);
}

rcl_ret_t handle_goal(rclc_action_goal_handle_t * goal_handle, void * context)
{
  (void) context;

  example_interfaces__action__Fibonacci_SendGoal_Request * req =
    (example_interfaces__action__Fibonacci_SendGoal_Request *) goal_handle->ros_goal_request;

  // Too big, rejecting

  if (req->goal.order > 200) {
    return RCL_RET_ACTION_GOAL_REJECTED;
  }

  th.start(callback(fibonacci_worker, goal_handle));

  return RCL_RET_ACTION_GOAL_ACCEPTED;
}

bool handle_cancel(rclc_action_goal_handle_t * goal_handle, void * context)
{
  (void) context;
  (void) goal_handle;

  return true;
}

void setup()
{
  set_microros_transports();

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  pinMode(LEDR, OUTPUT);
  digitalWrite(LEDR, HIGH);
  pinMode(LEDG, OUTPUT);
  digitalWrite(LEDG, HIGH);
  pinMode(LEDB, OUTPUT);
  digitalWrite(LEDB, HIGH);
  delay(1000);

  allocator = rcl_get_default_allocator();

  // create init_options
  RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));

  // create node
  RCCHECK(rclc_node_init_default(&node, "fibonacci_action_server_rcl", "", &support));
  // Create action service
  RCCHECK(
    rclc_action_server_init_default(
      &action_server,
      &node,
      &support,
      ROSIDL_GET_ACTION_TYPE_SUPPORT(example_interfaces, Fibonacci),
      "fibonacci"
    ));
  // Create executor
  RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));

  RCCHECK(
    rclc_executor_add_action_server(
      &executor,
      &action_server,
      10,
      ros_goal_request,
      sizeof(example_interfaces__action__Fibonacci_SendGoal_Request),
      handle_goal,
      handle_cancel,
      (void *) &action_server));
}

void loop()
{
  delay(100);
  mtx.lock();
  RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
  mtx.unlock();
}

@pablogs9
Copy link
Member

What is the status? This works but only once right?

@metanav
Copy link
Author

metanav commented Aug 10, 2022

Any order above 200 works good as many times since it returns before starting a thread:

$ ros2 action send_goal /fibonacci example_interfaces/Fibonacci "order: 800"
Waiting for an action server to become available...
Sending goal:
     order: 800

Goal was rejected.

This works only one time and hangs forever in the next calls.

$ ros2 action send_goal /fibonacci example_interfaces/Fibonacci "order: 5"

@pablogs9
Copy link
Member

I would recommend you to try to avoid the thread. Let's check if this solves the lock.

@pablogs9
Copy link
Member

Btw, you are calling start multiple times in the same thread object. Check documentation

image

@metanav
Copy link
Author

metanav commented Aug 10, 2022

I think every time a goal request comes a thread is started. Do you mean there should be only one thread running forever and accept argument using a queue? Or, the Thread th; should be inside the handle_goal function.

@pablogs9
Copy link
Member

A Thread object can only be started once. Please read the RTOS documentation

@metanav
Copy link
Author

metanav commented Aug 10, 2022

You are right. This was a Thread start issue. I was starting the same Thread object which worked once as expected but failed to start next time. I have moved the thread start call to the setup function so that it is started once and the fibonacci_worker has a forever running loop which checks for a goal_handle. It works this way but I do not like the shared global variable. Maybe I will change that with a single member data queue later.

Thread th; // Global
rclc_action_goal_handle_t *g_goal_handle = NULL; // Global

void setup()
{
  ...
  th.start(fibonacci_worker);
}

void fibonacci_worker()
{
  while (1) {
    rclc_action_goal_handle_t *goal_handle = g_goal_handle;

    if (!goal_handle) {
      continue;
    }
    
    // do processing
    ...
    g_goal_handle = NULL;
  }
}

rcl_ret_t handle_goal(rclc_action_goal_handle_t * goal_handle, void * context)
{
 ...

  g_goal_handle = goal_handle;

  return RCL_RET_ACTION_GOAL_ACCEPTED;
}

@pablogs9
Copy link
Member

Ok, Im closing

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

No branches or pull requests

2 participants