DDS-RPC 规范规定了如何在 DDS 之上构建 RPC。下面以一个 IDL 为例,讲述 RPC 的基本构成。

在下面的叙述中,将使用 RPC 服务的称为 Request/Client/Caller,将提供 RPC 服务的称为 Replay/Server/Callee。

共有类型

下面定义了一些公有类型:

  1. RPC 共有类型

module dds {
    typedef octet GuidPrefix_t[12];

    struct EntityId_t {
        octet entityKey[3];
        octet entityKind;
    };

    struct GUID_t {
        GuidPrefix_t guidPrefix;
        EntityId_t entityId;
    };

    struct SequenceNumber_t {
        long high;
        unsigned long low;
    };

    struct SampleIdentity
    {
        GUID_t writer_guid;
        SequenceNumber_t sequence_number;
    };

    module rpc {
        typedef octet UnknownOperation;
        typedef octet UnknownException;
        typedef octet UnusedMember;
        enum RemoteExceptionCode_t {
            REMOTE_EX_OK,
            REMOTE_EX_UNSUPPORTED,
            REMOTE_EX_INVALID_ARGUMENT,
            REMOTE_EX_OUT_OF_RESOURCES,
            REMOTE_EX_UNKNOWN_OPERATION,
            REMOTE_EX_UNKNOWN_EXCEPTION
        };

        typedef string<255> InstanceName;

        struct RequestHeader {(1)
            SampleIndentity_t requestId;
            InstanceName instanceName;
        };

        struct ReplyHeader {(2)
            dds::SampleIdentity relatedRequestId;
            dds::rpc::RemoteExceptionCode_t remoteEx;
        };
    } // module rpc
} // module dds
  1. RequestHeader 用于 Client。

  2. ReplyHeader 用于 Server。

IDL 接口文件

假设现在有这样一个 RPC IDL:

module robot {
    exception TooFast {};

    enum Command { START_COMMAND, STOP_COMMAND };

    struct Status {
        string msg;
    };

    @DDSService
    interface RobotControl {
        void command(Command com);
        float setSpeed(float speed) raises (TooFast);
        float getSpeed();
        void getStatus(out Status status);
    };
}; //module robot

Request 接口

通过上面的类型可以生成下面这样用于 Client 的类型:

module robot {
    struct RobotControl_command_In {
        Command com;
    };
    struct RobotControl_setSpeed_In {
        float speed;
    };
    struct RobotControl_getSpeed_In {
        dds::rpc::UnusedMember dummy;
    };
    struct RobotControl_getStatus_In {
        dds::rpc::UnusedMember dummy;
    };
}

这样的一个 IDL 描述了每个接口需要传入的参数。 由于是 Request 侧,因此只有入参会被写入 。如果没有入参,就以 dummy 代替。

通过上面的 IDL 可以生成一个 Request 接口的 IDL 文件:

module robot {
    const long RobotControl_command_Hash= HASH(“command”);
    const long RobotControl_setSpeed_Hash= HASH(“setSpeed”);
    const long RobotControl_getSpeed_Hash= HASH(“getSpeed”);
    const long RobotControl_getStatus_Hash = HASH(“getStatus”);

    union RobotControl_Call switch(long) {(1)
        default
            dds::rpc::UnknownOperation unknownOp;
        case RobotControl_command_Hash:
            RobotControl_command_In command;
        case RobotControl_setSpeed_Hash:
            RobotControl_setSpeed_In setSpeed;
        case RobotControl_getSpeed_Hash:
            RobotControl_getSpeed_In getSpeed;
        case RobotControl_getStatus_Hash:
            RobotControl_getStatus_In getStatus;
    };
}

module robot {
    struct RobotControl_Request {(2)
        dds::rpc::RequestHeader header;
        RobotControl_Call data;
    };
}
  1. 用于 Request 的 Body。

  2. 用于传入 DDS 的数据。

可以看到,IDL 将 IDL 接口文件 中定义的接口首先映射到四个枚举常量中,然后通过一个 TaggedUnion 来判断调用的是哪个接口。通过对应的接口就能够序列化相应的数据。最后连同 RPC Header 传递给 DDS 中。

Replay 接口

通过 IDL 接口文件 可以生成下面这样用于 Server 的类型:

  1. Replay 类型

module Robot {
    struct RobotControl_command_Out {
        dds::rpc::UnusedMember dummy;
    };
    struct RobotControl_setSpeed_Out {
        float return_;
    };
    struct RobotControl_getSpeed_Out {
        float return_;
    };
    struct RobotControl_getStatus_Out {
        Status status;
    };
}

module robot {
    const long TooFast_Ex_Hash = HASH (“TooFast”);
    union RobotControl_command_Result switch(long)
    {
        case dds::RETCODE_OK:
            RobotControl_Command_Out result;
    };
    union RobotControl_setSpeed_Result switch(long)
    {
        case dds::RETCODE_OK:
            RobotControl_setSpeed_Out result;
        case TooFast_Ex_Hash:
            TooFast toofast_ex;
    };
    union RobotControl_getSpeed_Result switch(long)
    {
        case dds::RETCODE_OK:
            RobotControl_getSpeed_Out result;
    };
    union RobotControl_getStatus_Result switch(long)
    {
        case dds::RETCODE_OK:
            RobotControl_getStatus_Out result;
    };
};// module robot

Server 类型定义特点如下:

  • Server 仅需要关注出参。如果函数没有返回值,则使用 dummy 代替。

  • Server 中每个参数的结果使用 TaggedUnion 标识,使用 Tag 表示函数调用结果是否正确(类似 Rust 中的 Result)。 - Server 生成的接口如下:

  1. Replay 接口

union RobotControl_Return switch (long)
{
    default:
        dds::rpc::UnknownOperation unknownOp;
    case RobotControl_command_Hash:
        RobotControl_command_Result command;
    case RobotControl_setSpeed_Hash:
        RobotControl_setSpeed_Result setSpeed;
    case RobotControl_getSpeed_Hash:
        RobotControl_getSpeed_Result getSpeed;
    case RobotControl_getStatus_Hash:
        RobotControl_getStatus_Result getStatus;
};

struct RobotControl_Reply {
    dds::rpc::ReplyHeader header;
    RobotControl_Return reply;
};

和 Client 相同,Server 也使用 TaggedUnion 传达函数调用结果。

错误码

RPC 在 DDS 原有的错误码(称为 LocalError) 上拓充了一部分错误码(称为 RemoteError)。这些错误码由下面进行定义:

RemoteError
module dds {
    module rpc {
        enum RemoteExceptionCode_t {
            REMOTE_EX_OK,
            REMOTE_EX_UNSUPPORTED,
            REMOTE_EX_INVALID_ARGUMENT,
            REMOTE_EX_OUT_OF_RESOURCES,
            REMOTE_EX_UNKNOWN_OPERATION,
            REMOTE_EX_UNKNOWN_EXCEPTION
        };
    } // module rpc
} // module dds
Remote Exception Code含意

REMOTE_EX_OK

The request was executed successfully.

REMOTE_EX_UNSUPPORTED

Operation is valid but it is unsupported (a.k.a. not implemented).

REMOTE_EX_INVALID_ARGUMENT

The value of the parameter passed has an illegal value (e.g., two options active in a @Choice structure).

REMOTE_EX_OUT_OF_RESOURCES

The remote service ran out of resources while processing the request.

REMOTE_EX_UNKNOWN_OPERATION

The operation called is unknown.

REMOTE_EX_UNKNOWN_EXCEPTION

A generic, unspecified exception was raised by the service implementation.

QoS

QoS 的默认值如下,这些值可以在 IDL 中通过 @QoS 的方式进行修改:

QoS PolicyValue

Reliability

DDS_RELIABLE_RELIABILITY_QOS

History

DDS_KEEP_ALL_HISTORY_QOS

Durability

DDS_VOLATILE_DURABILITY_QOS

API 风格

RPC over DDS 规定了两种调用风格:

  • Request/Reply 对 RPC 进行了最小实现。IDL 甚至只需要所需的结构体即可。但是这种风格不具备 RPC 的外观。

  • Function-call 风格提供了类似函数调用的实现,但是它无法胜任某些具备持续返回值的调用(比如进度条)。

即使是 Server/Client 使用不同的调用风格,两者之间也是能够正常通信的。一般而言,可以使用 Request/Reply 来实现 Function-call 风格。

由于 DDS 本身是一个 pub-sub 网络,是天然无状态的,因此需要 [共有类型] 中的 SampleIdentity(GUID, SN) 来标明函数调用。Client 需要用它来识别得到的响应是哪一次得到的,Server 需要使用它来表示这次回复是给谁的。

Last moify: 2025-04-07 07:03:40
Build time:2025-07-18 09:41:42
Powered By asphinx