xref: /aosp_15_r20/external/pigweed/pw_rpc/nanopb/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_rpc_nanopb:
2
3------
4nanopb
5------
6``pw_rpc`` can generate services which encode/decode RPC requests and responses
7as nanopb message structs.
8
9Usage
10=====
11To enable nanopb code generation, the build argument
12``dir_pw_third_party_nanopb`` must be set to point to a local nanopb
13installation. Nanopb 0.4 is recommended, but Nanopb 0.3 is also supported.
14
15Define a ``pw_proto_library`` containing the .proto file defining your service
16(and optionally other related protos), then depend on the ``nanopb_rpc``
17version of that library in the code implementing the service.
18
19.. code-block::
20
21   # chat/BUILD.gn
22
23   import("$dir_pw_build/target_types.gni")
24   import("$dir_pw_protobuf_compiler/proto.gni")
25
26   pw_proto_library("chat_protos") {
27     sources = [ "chat_protos/chat_service.proto" ]
28   }
29
30   # Library that implements the Chat service.
31   pw_source_set("chat_service") {
32     sources = [
33       "chat_service.cc",
34       "chat_service.h",
35     ]
36     public_deps = [ ":chat_protos.nanopb_rpc" ]
37   }
38
39A C++ header file is generated for each input .proto file, with the ``.proto``
40extension replaced by ``.rpc.pb.h``. For example, given the input file
41``chat_protos/chat_service.proto``, the generated header file will be placed
42at the include path ``"chat_protos/chat_service.rpc.pb.h"``.
43
44Generated code API
45==================
46All examples in this document use the following RPC service definition.
47
48.. code-block:: protobuf
49
50   // chat/chat_protos/chat_service.proto
51
52   syntax = "proto3";
53
54   service Chat {
55     // Returns information about a chatroom.
56     rpc GetRoomInformation(RoomInfoRequest) returns (RoomInfoResponse) {}
57
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) {}
61
62     // Uploads a file, in chunks, to a chatroom.
63     rpc UploadFile(stream UploadFileRequest) returns (UploadFileResponse) {}
64
65     // Sends messages to a chatroom while receiving messages from other users.
66     rpc Chat(stream ChatMessage) returns (stream ChatMessage) {}
67   }
68
69Server-side
70-----------
71A C++ class is generated for each service in the .proto file. The class is
72located within a special ``pw_rpc::nanopb`` sub-namespace of the file's package.
73
74The generated class is a base class which must be derived to implement the
75service's methods. The base class is templated on the derived class.
76
77.. code-block:: c++
78
79   #include "chat_protos/chat_service.rpc.pb.h"
80
81   class ChatService final : public pw_rpc::nanopb::Chat::Service<ChatService> {
82    public:
83     // Implementations of the service's RPC methods; see below.
84   };
85
86Unary RPC
87^^^^^^^^^
88A unary RPC is implemented as a function which takes in the RPC's request struct
89and populates a response struct to send back, with a status indicating whether
90the request succeeded.
91
92.. code-block:: c++
93
94   pw::Status GetRoomInformation(pw::rpc::
95                                 const RoomInfoRequest& request,
96                                 RoomInfoResponse& response);
97
98Server streaming RPC
99^^^^^^^^^^^^^^^^^^^^
100A server streaming RPC receives the client's request message alongside a
101``ServerWriter``, used to stream back responses.
102
103.. code-block:: c++
104
105   void ListUsersInRoom(pw::rpc::
106                        const ListUsersRequest& request,
107                        pw::rpc::ServerWriter<ListUsersResponse>& writer);
108
109The ``ServerWriter`` object is movable, and remains active until it is manually
110closed or goes out of scope. The writer has a simple API to return responses:
111
112.. cpp:function:: Status ServerWriter::Write(const T& response)
113
114  Writes a single response message to the stream. The returned status indicates
115  whether the write was successful.
116
117.. cpp:function:: void ServerWriter::Finish(Status status = OkStatus())
118
119  Closes the stream and sends back the RPC's overall status to the client.
120
121.. cpp:function:: Status ServerWriter::TryFinish(Status status = OkStatus())
122
123  Closes the stream and sends back the RPC's overall status to the client only
124  if the final packet is successfully sent.
125
126Once a ``ServerWriter`` has been closed, all future ``Write`` calls will fail.
127
128.. attention::
129
130  Make sure to use ``std::move`` when passing the ``ServerWriter`` around to
131  avoid accidentally closing it and ending the RPC.
132
133Client streaming RPC
134^^^^^^^^^^^^^^^^^^^^
135.. attention:: Supported, but the documentation is still under construction.
136
137Bidirectional streaming RPC
138^^^^^^^^^^^^^^^^^^^^^^^^^^^
139.. attention:: Supported, but the documentation is still under construction.
140
141Client-side
142-----------
143A corresponding client class is generated for every service defined in the proto
144file. To allow multiple types of clients to exist, it is placed under the
145``pw_rpc::nanopb`` namespace. The ``Client`` class is nested under
146``pw_rpc::nanopb::ServiceName``. For example, the ``Chat`` service would create
147``pw_rpc::nanopb::Chat::Client``.
148
149Service clients are instantiated with a reference to the RPC client through
150which they will send requests, and the channel ID they will use.
151
152.. code-block:: c++
153
154   // Nested under pw_rpc::nanopb::ServiceName.
155   class Client {
156    public:
157     Client(::pw::rpc::Client& client, uint32_t channel_id);
158
159     pw::rpc::NanopbUnaryReceiver<RoomInfoResponse> GetRoomInformation(
160         const RoomInfoRequest& request,
161         ::pw::Function<void(Status, const RoomInfoResponse&)> on_response,
162         ::pw::Function<void(Status)> on_rpc_error = nullptr);
163
164     // ...and more (see below).
165   };
166
167RPCs can also be invoked individually as free functions:
168
169.. code-block:: c++
170
171   pw::rpc::NanopbUnaryReceiver<RoomInfoResponse> call = pw_rpc::nanopb::Chat::GetRoomInformation(
172       client, channel_id, request, on_response, on_rpc_error);
173
174The client class has member functions for each method defined within the
175service's protobuf descriptor. The arguments to these methods vary depending on
176the type of RPC. Each method returns a client call object which stores the
177context of the ongoing RPC call. For more information on call objects, refer to
178the :ref:`core RPC docs <module-pw_rpc-making-calls>`.
179
180.. admonition:: Callback invocation
181
182  RPC callbacks are invoked synchronously from ``Client::ProcessPacket``.
183
184Method APIs
185^^^^^^^^^^^
186The arguments provided when invoking a method depend on its type.
187
188Unary RPC
189~~~~~~~~~
190A unary RPC call takes the request struct and a callback to invoke when a
191response is received. The callback receives the RPC's status and response
192struct.
193
194An optional second callback can be provided to handle internal errors.
195
196.. code-block:: c++
197
198   pw::rpc::NanopbUnaryReceiver<RoomInfoResponse> GetRoomInformation(
199       const RoomInfoRequest& request,
200       ::pw::Function<void(const RoomInfoResponse&, Status)> on_response,
201       ::pw::Function<void(Status)> on_rpc_error = nullptr);
202
203Server streaming RPC
204~~~~~~~~~~~~~~~~~~~~
205A server streaming RPC call takes the initial request struct and two callbacks.
206The first is invoked on every stream response received, and the second is
207invoked once the stream is complete with its overall status.
208
209An optional third callback can be provided to handle internal errors.
210
211.. code-block:: c++
212
213   pw::rpc::NanopbClientReader<ListUsersResponse> ListUsersInRoom(
214       const ListUsersRequest& request,
215       ::pw::Function<void(const ListUsersResponse&)> on_response,
216       ::pw::Function<void(Status)> on_stream_end,
217       ::pw::Function<void(Status)> on_rpc_error = nullptr);
218
219Client streaming RPC
220~~~~~~~~~~~~~~~~~~~~
221.. attention:: Supported, but the documentation is still under construction.
222
223Bidirectional streaming RPC
224~~~~~~~~~~~~~~~~~~~~~~~~~~~
225.. attention:: Supported, but the documentation is still under construction.
226
227Example usage
228^^^^^^^^^^^^^
229The following example demonstrates how to call an RPC method using a nanopb
230service client and receive the response.
231
232.. code-block:: c++
233
234   #include "chat_protos/chat_service.rpc.pb.h"
235
236   namespace {
237
238     using ChatClient = pw_rpc::nanopb::Chat::Client;
239
240     MyChannelOutput output;
241     pw::rpc::Channel channels[] = {pw::rpc::Channel::Create<1>(&output)};
242     pw::rpc::Client client(channels);
243
244     // Callback function for GetRoomInformation.
245     void LogRoomInformation(const RoomInfoResponse& response, Status status);
246
247   }  // namespace
248
249   void InvokeSomeRpcs() {
250     // Instantiate a service client to call Chat service methods on channel 1.
251     ChatClient chat_client(client, 1);
252
253     // The RPC will remain active as long as `call` is alive.
254     auto call = chat_client.GetRoomInformation(
255         {.room = "pigweed"}, LogRoomInformation);
256     if (!call.active()) {
257       // The invocation may fail. This could occur due to an invalid channel ID,
258       // for example. The failure status is forwarded to the to call's
259       // on_rpc_error callback.
260       return;
261     }
262
263     // For simplicity, block until the call completes. An actual implementation
264     // would likely std::move the call somewhere to keep it active while doing
265     // other work.
266     while (call.active()) {
267       Wait();
268     }
269
270     // Do other stuff now that we have the room information.
271   }
272
273Zephyr
274======
275To enable ``pw_rpc.nanopb.*`` for Zephyr add ``CONFIG_PIGWEED_RPC_NANOPB=y`` to
276the project's configuration. This will enable the Kconfig menu for the
277following:
278
279* ``pw_rpc.nanopb.method`` which can be enabled via
280  ``CONFIG_PIGWEED_RPC_NANOPB_METHOD=y``.
281* ``pw_rpc.nanopb.method_union`` which can be enabled via
282  ``CONFIG_PIGWEED_RPC_NANOPB_METHOD_UNION=y``.
283* ``pw_rpc.nanopb.client`` which can be enabled via
284  ``CONFIG_PIGWEED_RPC_NANOPB_CLIENT=y``.
285* ``pw_rpc.nanopb.common`` which can be enabled via
286  ``CONFIG_PIGWEED_RPC_NANOPB_COMMON=y``.
287* ``pw_rpc.nanopb.echo_service`` which can be enabled via
288  ``CONFIG_PIGWEED_RPC_NANOPB_ECHO_SERVICE=y``.
289