1 .. _module-pw_rpc_nanopb:
6 ``pw_rpc`` can generate services which encode/decode RPC requests and responses
7 as nanopb message structs.
11 To enable nanopb code generation, the build argument
12 ``dir_pw_third_party_nanopb`` must be set to point to a local nanopb
15 Define a ``pw_proto_library`` containing the .proto file defining your service
16 (and optionally other related protos), then depend on the ``nanopb_rpc``
17 version of that library in the code implementing the service.
23 import("$dir_pw_build/target_types.gni")
24 import("$dir_pw_protobuf_compiler/proto.gni")
26 pw_proto_library("chat_protos") {
27 sources = [ "chat_protos/chat_service.proto" ]
30 # Library that implements the ChatService.
31 pw_source_set("chat_service") {
36 public_deps = [ ":chat_protos.nanopb_rpc" ]
39 A C++ header file is generated for each input .proto file, with the ``.proto``
40 extension replaced by ``.rpc.pb.h``. For example, given the input file
41 ``chat_protos/chat_service.proto``, the generated header file will be placed
42 at the include path ``"chat_protos/chat_service.rpc.pb.h"``.
46 Take the following RPC service as an example.
50 // chat/chat_protos/chat_service.proto
55 // Returns information about a chatroom.
56 rpc GetRoomInformation(RoomInfoRequest) returns (RoomInfoResponse) {}
58 // Lists all of the users in a chatroom. The response is streamed as there
59 // may be a large amount of users.
60 rpc ListUsersInRoom(ListUsersRequest) returns (stream ListUsersResponse) {}
62 // Uploads a file, in chunks, to a chatroom.
63 rpc UploadFile(stream UploadFileRequest) returns (UploadFileResponse) {}
65 // Sends messages to a chatroom while receiving messages from other users.
66 rpc Chat(stream ChatMessage) returns (stream ChatMessage) {}
71 A C++ class is generated for each service in the .proto file. The class is
72 located within a special ``generated`` sub-namespace of the file's package.
74 The generated class is a base class which must be derived to implement the
75 service's methods. The base class is templated on the derived class.
79 #include "chat_protos/chat_service.rpc.pb.h"
81 class ChatService final : public generated::ChatService<ChatService> {
83 // Implementations of the service's RPC methods; see below.
88 A unary RPC is implemented as a function which takes in the RPC's request struct
89 and populates a response struct to send back, with a status indicating whether
90 the request succeeded.
94 pw::Status GetRoomInformation(pw::rpc::ServerContext& ctx,
95 const RoomInfoRequest& request,
96 RoomInfoResponse& response);
100 A server streaming RPC receives the client's request message alongside a
101 ``ServerWriter``, used to stream back responses.
105 void ListUsersInRoom(pw::rpc::ServerContext& ctx,
106 const ListUsersRequest& request,
107 pw::rpc::ServerWriter<ListUsersResponse>& writer);
109 The ``ServerWriter`` object is movable, and remains active until it is manually
110 closed or goes out of scope. The writer has a simple API to return responses:
112 .. cpp:function:: Status ServerWriter::Write(const T& response)
114 Writes a single response message to the stream. The returned status indicates
115 whether the write was successful.
117 .. cpp:function:: void ServerWriter::Finish(Status status = OkStatus())
119 Closes the stream and sends back the RPC's overall status to the client.
121 Once a ``ServerWriter`` has been closed, all future ``Write`` calls will fail.
125 Make sure to use ``std::move`` when passing the ``ServerWriter`` around to
126 avoid accidentally closing it and ending the RPC.
132 ``pw_rpc`` does not yet support client streaming RPCs.
134 Bidirectional streaming RPC
135 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
138 ``pw_rpc`` does not yet support bidirectional streaming RPCs.
142 A corresponding client class is generated for every service defined in the proto
143 file. Like the service class, it is placed under the ``generated`` namespace.
144 The class is named after the service, with a ``Client`` suffix. For example, the
145 ``ChatService`` would create a ``generated::ChatServiceClient``.
147 The client class contains static methods to call each of the service's methods.
148 It is not meant to be instantiated. The signatures for the methods all follow
149 the same format, taking a channel through which to communicate, the initial
150 request struct, and a response handler.
154 static NanopbClientCall<UnaryResponseHandler<RoomInfoResponse>>
155 GetRoomInformation(Channel& channel,
156 const RoomInfoRequest& request,
157 UnaryResponseHandler<RoomInfoResponse> handler);
159 The ``NanopbClientCall`` object returned by the RPC invocation stores the active
160 RPC's context. For more information on ``ClientCall`` objects, refer to the
161 :ref:`core RPC documentation <module-pw_rpc-making-calls>`.
165 RPC responses are sent back to the caller through a response handler object.
166 These are classes with virtual callback functions implemented by the RPC caller
167 to handle RPC events.
169 There are two types of response handlers: unary and server-streaming, which are
170 used depending whether the method's responses are a stream or not.
172 Unary / client streaming RPC
173 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
174 A ``UnaryResponseHandler`` is used by methods where the server returns a single
175 response. It contains a callback for the response, which is only called once.
179 template <typename Response>
180 class UnaryResponseHandler {
182 virtual ~UnaryResponseHandler() = default;
184 // Called when the response is received from the server with the method's
185 // status and the deserialized response struct.
186 virtual void ReceivedResponse(Status status, const Response& response) = 0;
188 // Called when an error occurs internally in the RPC client or server.
189 virtual void RpcError(Status) {}
192 .. cpp:class:: template <typename Response> UnaryResponseHandler
194 A handler for RPC methods which return a single response (i.e. unary and
197 .. cpp:function:: virtual void UnaryResponseHandler::ReceivedResponse(Status status, const Response& response)
199 Callback invoked when the response is recieved from the server. Guaranteed to
202 .. cpp:function:: virtual void UnaryResponseHandler::RpcError(Status status)
204 Callback invoked if an internal error occurs in the RPC system. Optional;
207 **Example implementation**
211 class RoomInfoHandler : public UnaryResponseHandler<RoomInfoResponse> {
213 void ReceivedResponse(Status status,
214 const RoomInfoResponse& response) override {
216 response_ = response;
220 constexpr RoomInfoResponse& response() { return response_; }
223 RoomInfoResponse response_;
226 Server streaming / bidirectional streaming RPC
227 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
228 For methods which return a response stream, a ``ServerStreamingResponseHandler``
233 class ServerStreamingResponseHandler {
235 virtual ~ServerStreamingResponseHandler() = default;
237 // Called on every response received from the server with the deserialized
239 virtual void ReceivedResponse(const Response& response) = 0;
241 // Called when the server ends the stream with the overall RPC status.
242 virtual void Complete(Status status) = 0;
244 // Called when an error occurs internally in the RPC client or server.
245 virtual void RpcError(Status) {}
248 .. cpp:class:: template <typename Response> ServerStreamingResponseHandler
250 A handler for RPC methods which return zero or more responses (i.e. server
251 and bidirectional streaming).
253 .. cpp:function:: virtual void ServerStreamingResponseHandler::ReceivedResponse(const Response& response)
255 Callback invoked whenever a response is received from the server.
257 .. cpp:function:: virtual void ServerStreamingResponseHandler::Complete(Status status)
259 Callback invoked when the server ends the stream, with the overall status for
262 .. cpp:function:: virtual void ServerStreamingResponseHandler::RpcError(Status status)
264 Callback invoked if an internal error occurs in the RPC system. Optional;
267 **Example implementation**
271 class ChatHandler : public UnaryResponseHandler<ChatMessage> {
273 void ReceivedResponse(const ChatMessage& response) override {
274 gui_.RenderChatMessage(response);
277 void Complete(Status status) override {
278 client_.Exit(status);
288 The following example demonstrates how to call an RPC method using a nanopb
289 service client and receive the response.
293 #include "chat_protos/chat_service.rpc.pb.h"
296 MyChannelOutput output;
297 pw::rpc::Channel channels[] = {pw::rpc::Channel::Create<0>(&output)};
298 pw::rpc::Client client(channels);
301 void InvokeSomeRpcs() {
302 RoomInfoHandler handler;
304 // The RPC will remain active as long as `call` is alive.
305 auto call = ChatServiceClient::GetRoomInformation(channels[0],
309 // For simplicity, block here. An actual implementation would likely
310 // std::move the call somewhere to keep it active while doing other work.
311 while (call.active()) {
315 DoStuff(handler.response());