ros nodehandle在JNI回调中

发布于 2025-02-09 23:08:01 字数 1441 浏览 0 评论 0原文

我需要在Spring Boot应用程序中接收ROS消息。为此,我设置了一些JNI课程。它有效,但是一旦我创建了一个节点,我就无法再用一个普通的sigint关闭该应用程序,就需要一个sigkill。

这是代码:

cppbridge.java

public class CppBridge {

    static {
        System.load(new File("backend/src/main/cpp/libcppbridge.so").getAbsolutePath());
    }

    public native void start();
}

cppbridge.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_ifremer_rospipe_CppBridge */

#ifndef _Included_org_ifremer_rospipe_CppBridge
#define _Included_org_ifremer_rospipe_CppBridge
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_ifremer_rospipe_CppBridge
 * Method:    start
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_ifremer_rospipe_CppBridge_start
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

cppbridge.cpp

JNIEXPORT void JNICALL Java_org_ifremer_rospipe_CppBridge_start(JNIEnv* env, jobject obj)
{
    // Init ROS:
    int argc = 0;
    ros::init(argc, nullptr, "ifr_mimosa_bridge");

    // Create the NodeHandle, this causes SIGINT to hang:
    ros::NodeHandle nh("~");
}

rospipe.java,

@Component
public class RosPipe {

    private final CppBridge mCppBridge = new CppBridge();

    @PostConstruct()
    public void onStart() {

        mCppBridge.start();
    }
}

为什么创建NodeHandle会阻止Cppbridge实例的破坏?

I need to receive ROS messages inside a Spring Boot application. For that I have setup some JNI classes. It works but as soon as I create a NodeHandle, I can no longer close the app with a normal SIGINT, it requires a SIGKILL.

Here's the code:

CppBridge.java

public class CppBridge {

    static {
        System.load(new File("backend/src/main/cpp/libcppbridge.so").getAbsolutePath());
    }

    public native void start();
}

CppBridge.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_ifremer_rospipe_CppBridge */

#ifndef _Included_org_ifremer_rospipe_CppBridge
#define _Included_org_ifremer_rospipe_CppBridge
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_ifremer_rospipe_CppBridge
 * Method:    start
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_ifremer_rospipe_CppBridge_start
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

CppBridge.cpp

JNIEXPORT void JNICALL Java_org_ifremer_rospipe_CppBridge_start(JNIEnv* env, jobject obj)
{
    // Init ROS:
    int argc = 0;
    ros::init(argc, nullptr, "ifr_mimosa_bridge");

    // Create the NodeHandle, this causes SIGINT to hang:
    ros::NodeHandle nh("~");
}

RosPipe.java

@Component
public class RosPipe {

    private final CppBridge mCppBridge = new CppBridge();

    @PostConstruct()
    public void onStart() {

        mCppBridge.start();
    }
}

Why does the creation of a NodeHandle blocks the destruction of the CppBridge instance?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

瞎闹 2025-02-16 23:08:01

事实证明,内部ROS拦截了Sigint信号,因此Spring不再接收它。因此,按下CTRL-C时,C ++过程在Java保持活力时优雅地终止。

为了修复它,我覆盖了ROS Sigint处理,以便在ROS终止后通知Spring:

void Node::init(JNIEnv* javaBridgeEnv)
{
    // Get a reference to the JVM:
    if (javaBridgeEnv == nullptr || javaBridgeEnv->GetJavaVM(&mJvm) != 0)
    {
        mJvm = nullptr;
        std::cerr << "Unable to get Java Virtual Machine." << std::endl;
    }

    // Init ROS:
    int argc = 0;
    ros::init(argc, nullptr, "test", ros::init_options::NoSigintHandler);
    signal(SIGINT, Node::onSigint);

    ros::NodeHandle nh_private;

    [...]

    ros::spin();
}

void Node::onSigint(int)
{
    // Shutdown ROS:
    ros::shutdown();

    // Notify Spring, otherwise it won't catch the SIGINT and won't exit:
    if (mJvm != nullptr)
    {
        JNIEnv* env;
        mJvm->AttachCurrentThread((void **)&env, nullptr);

        // To get the methods signatures: javap -s -p path/to/File.class
        jclass classCppBridge = env->FindClass("org/ifremer/rospipe/RosPipe");
        jmethodID stopMethod = env->GetStaticMethodID(classCppBridge, "stop", "()V");

        env->CallStaticVoidMethod(classCppBridge, stopMethod);
    }
}

在Java侧:

public static void stop() {
    int exitCode = SpringApplication.exit(mApplicationContext);
    System.exit(exitCode);
}

Turned out that internally ROS intercepts the SIGINT signal, and thus Spring no longer receives it. So when pressing CTRL-C the C++ process terminates gracefully while the Java stays alive.

To fix it I overrode ROS SIGINT handling in order to notify Spring after ROS is terminated:

void Node::init(JNIEnv* javaBridgeEnv)
{
    // Get a reference to the JVM:
    if (javaBridgeEnv == nullptr || javaBridgeEnv->GetJavaVM(&mJvm) != 0)
    {
        mJvm = nullptr;
        std::cerr << "Unable to get Java Virtual Machine." << std::endl;
    }

    // Init ROS:
    int argc = 0;
    ros::init(argc, nullptr, "test", ros::init_options::NoSigintHandler);
    signal(SIGINT, Node::onSigint);

    ros::NodeHandle nh_private;

    [...]

    ros::spin();
}

void Node::onSigint(int)
{
    // Shutdown ROS:
    ros::shutdown();

    // Notify Spring, otherwise it won't catch the SIGINT and won't exit:
    if (mJvm != nullptr)
    {
        JNIEnv* env;
        mJvm->AttachCurrentThread((void **)&env, nullptr);

        // To get the methods signatures: javap -s -p path/to/File.class
        jclass classCppBridge = env->FindClass("org/ifremer/rospipe/RosPipe");
        jmethodID stopMethod = env->GetStaticMethodID(classCppBridge, "stop", "()V");

        env->CallStaticVoidMethod(classCppBridge, stopMethod);
    }
}

In the Java side:

public static void stop() {
    int exitCode = SpringApplication.exit(mApplicationContext);
    System.exit(exitCode);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文