micro-ROS / micro_ros_arduino

micro-ROS library for Arduino
Apache License 2.0
443 stars 114 forks source link

Undefined reference to rclc_lifecycle functions #907

Closed tnagyzambo closed 2 years ago

tnagyzambo commented 2 years ago

Issue template

Steps to reproduce the issue

Attempt to compile minimal lifecycle example:

#include <micro_ros_arduino.h>

#include <rcl/error_handling.h>
#include <rcutils/logging_macros.h>

#include <lifecycle_msgs/msg/transition_description.h>
#include <lifecycle_msgs/msg/transition_event.h>
#include <lifecycle_msgs/srv/change_state.h>
#include <lifecycle_msgs/srv/get_available_states.h>
#include <lifecycle_msgs/srv/get_available_transitions.h>
#include <lifecycle_msgs/srv/get_state.h>

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

#include <rclc_lifecycle/rclc_lifecycle.h>

#define LED_PIN 13

#define RCCHECK(fn)                                                                                                    \
    {                                                                                                                  \
        rcl_ret_t temp_rc = fn;                                                                                        \
        if ((temp_rc != RCL_RET_OK)) {                                                                                 \
            printf("Failed status on line %d: %d. Aborting.\n", __LINE__, (int)temp_rc);                               \
            return 1;                                                                                                  \
        }                                                                                                              \
    }

#define RCSOFTCHECK(fn)                                                                                                \
    {                                                                                                                  \
        rcl_ret_t temp_rc = fn;                                                                                        \
        if ((temp_rc != RCL_RET_OK)) {                                                                                 \
            printf("Failed status on line %d: %d. Continuing.\n", __LINE__, (int)temp_rc);                             \
        }                                                                                                              \
    }

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

int my_on_configure() {
    printf("  >>> lifecycle_node: on_configure() callback called.\n");
    return RCL_RET_OK;
}

int my_on_activate() {
    printf("  >>> lifecycle_node: on_activate() callback called.\n");
    return RCL_RET_OK;
}

int my_on_deactivate() {
    printf("  >>> lifecycle_node: on_deactivate() callback called.\n");
    return RCL_RET_OK;
}

int my_on_cleanup() {
    printf("  >>> lifecycle_node: on_cleanup() callback called.\n");
    return RCL_RET_OK;
}

int main() {
    byte arduino_mac[] = {0xAA, 0xBB, 0xCC, 0xEE, 0xDD, 0xFF};
    IPAddress arduino_ip(192, 168, 2, 2);
    IPAddress agent_ip(192, 168, 2, 1);
    set_microros_native_ethernet_udp_transports(arduino_mac, arduino_ip, agent_ip, 8090);

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

    delay(2000);

    rcl_allocator_t allocator = rcl_get_default_allocator();
    rclc_support_t support;
    rcl_ret_t rc;

    // create init_options
    rc = rclc_support_init(&support, 0, NULL, &allocator);
    if (rc != RCL_RET_OK) {
        printf("Error rclc_support_init.\n");
        return -1;
    }

    // create rcl_node
    rcl_node_t my_node;
    rc = rclc_node_init_default(&my_node, "lifecycle_node", "rclc", &support);
    if (rc != RCL_RET_OK) {
        printf("Error in rclc_node_init_default\n");
        return -1;
    }

    // make it a lifecycle node
    printf("creating lifecycle node...\n");
    rcl_lifecycle_state_machine_t state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine();
    rclc_lifecycle_node_t lifecycle_node;
    rc = rclc_make_node_a_lifecycle_node(&lifecycle_node, &my_node, &state_machine_, &allocator, true);
    if (rc != RCL_RET_OK) {
        printf("Error in creating lifecycle node.\n");
        return -1;
    }

    // Executor
    rclc_executor_t executor;
    RCCHECK(rclc_executor_init(&executor,
                               &support.context,
                               4, // 1 for the node + 1 for each lifecycle service
                               &allocator));

    unsigned int rcl_wait_timeout = 1000; // in ms
    RCCHECK(rclc_executor_set_timeout(&executor, RCL_MS_TO_NS(rcl_wait_timeout)));

    // Register lifecycle services
    printf("registering lifecycle services...\n");
    RCCHECK(rclc_lifecycle_init_get_state_server(&lifecycle_node, &executor));
    RCCHECK(rclc_lifecycle_init_get_available_states_server(&lifecycle_node, &executor));
    RCCHECK(rclc_lifecycle_init_change_state_server(&lifecycle_node, &executor));

    // Register lifecycle service callbacks
    printf("registering callbacks...\n");
    rclc_lifecycle_register_on_configure(&lifecycle_node, &my_on_configure);
    rclc_lifecycle_register_on_activate(&lifecycle_node, &my_on_activate);
    rclc_lifecycle_register_on_deactivate(&lifecycle_node, &my_on_deactivate);
    rclc_lifecycle_register_on_cleanup(&lifecycle_node, &my_on_cleanup);

    // Run
    RCSOFTCHECK(rclc_executor_spin(&executor));

    // Cleanup
    printf("cleaning up...\n");
    rc = rclc_lifecycle_node_fini(&lifecycle_node, &allocator);
    rc += rcl_node_fini(&my_node);
    rc += rclc_executor_fini(&executor);
    rc += rclc_support_fini(&support);

    if (rc != RCL_RET_OK) {
        printf("Error while cleaning up!\n");
        return -1;
    }
    return 0;
}

All lifecycle functions are undefined when attempting to build:

Processing portenta_h7_m7 (platform: ststm32; board: portenta_h7_m7; framework: arduino)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/ststm32/portenta_h7_m7.html
PLATFORM: ST STM32 (15.2.0) > Arduino Portenta H7 (M7 core)
HARDWARE: STM32H747XIH6 480MHz, 511.35KB RAM, 768KB Flash
DEBUG: Current (cmsis-dap) External (cmsis-dap, jlink, stlink)
PACKAGES: 
 - framework-arduino-mbed 2.6.1 
 - tool-dfuutil 1.9.200310 
 - toolchain-gccarmnoneeabi 1.70201.0 (7.2.1)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 32 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <micro_ros_arduino> 2.0.4-galactic+sha.3660860
|   |-- <Ethernet> 1.0.0
|   |   |-- <SocketWrapper> 1.0
|   |-- <SPI>
|   |-- <WiFi> 1.0
|   |   |-- <SocketWrapper> 1.0
Building in release mode
Linking .pio/build/portenta_h7_m7/firmware.elf
.pio/build/portenta_h7_m7/src/main.cpp.o: In function `main':
main.cpp:(.text.startup.main+0xfc): undefined reference to `rclc_make_node_a_lifecycle_node(rclc_lifecycle_node_t*, rcl_node_t*, rcl_lifecycle_state_machine_t*, rcutils_allocator_t*, bool)'
main.cpp:(.text.startup.main+0x144): undefined reference to `rclc_lifecycle_init_get_state_server(rclc_lifecycle_node_t*, rclc_executor_t*)'
main.cpp:(.text.startup.main+0x154): undefined reference to `rclc_lifecycle_init_get_available_states_server(rclc_lifecycle_node_t*, rclc_executor_t*)'
main.cpp:(.text.startup.main+0x164): undefined reference to `rclc_lifecycle_init_change_state_server(rclc_lifecycle_node_t*, rclc_executor_t*)'
main.cpp:(.text.startup.main+0x17a): undefined reference to `rclc_lifecycle_register_on_configure(rclc_lifecycle_node_t*, int (*)())'
main.cpp:(.text.startup.main+0x182): undefined reference to `rclc_lifecycle_register_on_activate(rclc_lifecycle_node_t*, int (*)())'
main.cpp:(.text.startup.main+0x18a): undefined reference to `rclc_lifecycle_register_on_deactivate(rclc_lifecycle_node_t*, int (*)())'
main.cpp:(.text.startup.main+0x192): undefined reference to `rclc_lifecycle_register_on_cleanup(rclc_lifecycle_node_t*, int (*)())'
main.cpp:(.text.startup.main+0x1b2): undefined reference to `rclc_lifecycle_node_fini(rclc_lifecycle_node_t*, rcutils_allocator_t*)'
collect2: error: ld returned 1 exit status
*** [.pio/build/portenta_h7_m7/firmware.elf] Error 1
=============================================================================== [FAILED] Took 2.92 seconds ===============================================================================

Additional information

platformio.ini with fixed linker script:

# There are issues using platformio link against the static H7 uros library
# REFERENCE: https://github.com/micro-ROS/micro_ros_arduino/issues/774

[env:portenta_h7_m7]
platform = ststm32
board = portenta_h7_m7
framework = arduino
extra_scripts = fix_linker.py
monitor_speed = 9600
build_flags =
    -L ./.pio/libdeps/portenta_h7_m7/micro_ros_arduino/src/cortex-m7/fpv5-d16-softfp/
    -D TARGET_PORTENTA_H7_M7

lib_deps =
    https://github.com/micro-ROS/micro_ros_arduino

Not sure if it is helpful but running nm on libmicroros.a results in:

00000000 T rclc_lifecycle_change_state
00000000 T rclc_lifecycle_change_state_callback
00000000 T rclc_lifecycle_execute_callback
00000000 T rclc_lifecycle_get_available_states_callback
00000000 T rclc_lifecycle_get_state_callback
00000000 T rclc_lifecycle_init_change_state_server
00000000 T rclc_lifecycle_init_get_available_states_server
00000000 T rclc_lifecycle_init_get_state_server
00000000 T rclc_lifecycle_node_fini
00000000 T rclc_lifecycle_register_callback
00000000 T rclc_lifecycle_register_on_activate
00000000 T rclc_lifecycle_register_on_cleanup
00000000 T rclc_lifecycle_register_on_configure
00000000 T rclc_lifecycle_register_on_deactivate
00000000 T rclc_make_node_a_lifecycle_node
pablogs9 commented 2 years ago

rclc_lifecycle is not built in the micro-ROS library, check here

You can copy it to extra_packages folder and try to build it, but it is not tested nor validated in MCU environments. Maybe the maintainer @JanStaschulat can tell us more details.

tnagyzambo commented 2 years ago

Thanks for the reply.

Unfortunately your suggestion does not work as there are package name conflicts.

ERROR:colcon:colcon build: Duplicate package names not supported:
- rclc_lifecycle:
  - extra_packages/rclc/rclc_lifecycle
  - uros/rclc/rclc_lifecycle

From my reading of the micro ros project website it suggests that this feature should be ready to use. Maybe there is a misunderstanding on my part. Since it seems that this is not limited to the Arduino library, is there a more appropriate place to continue this discussion?

pablogs9 commented 2 years ago

From my reading of the micro ros project website it suggests that this feature should be ready to use. Maybe there is a misunderstanding on my part. Since it seems that this is not limited to the Arduino library, is there a more appropriate place to continue this discussion?

The rclc repo

pablogs9 commented 2 years ago

Reopening until https://github.com/ros2/rclc/pull/279 is merged and library is supported under C++ build

pablogs9 commented 2 years ago

https://github.com/ros2/rclc/pull/279 merged