• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::
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:: 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:: 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:: 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:: 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
121Once a ``ServerWriter`` has been closed, all future ``Write`` calls will fail.
122
123.. attention::
124
125  Make sure to use ``std::move`` when passing the ``ServerWriter`` around to
126  avoid accidentally closing it and ending the RPC.
127
128Client streaming RPC
129^^^^^^^^^^^^^^^^^^^^
130.. attention:: Supported, but the documentation is still under construction.
131
132Bidirectional streaming RPC
133^^^^^^^^^^^^^^^^^^^^^^^^^^^
134.. attention:: Supported, but the documentation is still under construction.
135
136Client-side
137-----------
138A corresponding client class is generated for every service defined in the proto
139file. To allow multiple types of clients to exist, it is placed under the
140``pw_rpc::nanopb`` namespace. The ``Client`` class is nested under
141``pw_rpc::nanopb::ServiceName``. For example, the ``Chat`` service would create
142``pw_rpc::nanopb::Chat::Client``.
143
144Service clients are instantiated with a reference to the RPC client through
145which they will send requests, and the channel ID they will use.
146
147.. code-block:: c++
148
149  // Nested under pw_rpc::nanopb::ServiceName.
150  class Client {
151   public:
152    Client(::pw::rpc::Client& client, uint32_t channel_id);
153
154    pw::rpc::NanopbUnaryReceiver<RoomInfoResponse> GetRoomInformation(
155        const RoomInfoRequest& request,
156        ::pw::Function<void(Status, const RoomInfoResponse&)> on_response,
157        ::pw::Function<void(Status)> on_rpc_error = nullptr);
158
159    // ...and more (see below).
160  };
161
162RPCs can also be invoked individually as free functions:
163
164.. code-block:: c++
165
166    pw::rpc::NanopbUnaryReceiver<RoomInfoResponse> call = pw_rpc::nanopb::Chat::GetRoomInformation(
167        client, channel_id, request, on_response, on_rpc_error);
168
169The client class has member functions for each method defined within the
170service's protobuf descriptor. The arguments to these methods vary depending on
171the type of RPC. Each method returns a client call object which stores the
172context of the ongoing RPC call. For more information on call objects, refer to
173the :ref:`core RPC docs <module-pw_rpc-making-calls>`.
174
175.. admonition:: Callback invocation
176
177  RPC callbacks are invoked synchronously from ``Client::ProcessPacket``.
178
179Method APIs
180^^^^^^^^^^^
181The arguments provided when invoking a method depend on its type.
182
183Unary RPC
184~~~~~~~~~
185A unary RPC call takes the request struct and a callback to invoke when a
186response is received. The callback receives the RPC's status and response
187struct.
188
189An optional second callback can be provided to handle internal errors.
190
191.. code-block:: c++
192
193  pw::rpc::NanopbUnaryReceiver<RoomInfoResponse> GetRoomInformation(
194      const RoomInfoRequest& request,
195      ::pw::Function<void(const RoomInfoResponse&, Status)> on_response,
196      ::pw::Function<void(Status)> on_rpc_error = nullptr);
197
198Server streaming RPC
199~~~~~~~~~~~~~~~~~~~~
200A server streaming RPC call takes the initial request struct and two callbacks.
201The first is invoked on every stream response received, and the second is
202invoked once the stream is complete with its overall status.
203
204An optional third callback can be provided to handle internal errors.
205
206.. code-block:: c++
207
208  pw::rpc::NanopbClientReader<ListUsersResponse> ListUsersInRoom(
209      const ListUsersRequest& request,
210      ::pw::Function<void(const ListUsersResponse&)> on_response,
211      ::pw::Function<void(Status)> on_stream_end,
212      ::pw::Function<void(Status)> on_rpc_error = nullptr);
213
214Client streaming RPC
215~~~~~~~~~~~~~~~~~~~~
216.. attention:: Supported, but the documentation is still under construction.
217
218Bidirectional streaming RPC
219~~~~~~~~~~~~~~~~~~~~~~~~~~~
220.. attention:: Supported, but the documentation is still under construction.
221
222Example usage
223^^^^^^^^^^^^^
224The following example demonstrates how to call an RPC method using a nanopb
225service client and receive the response.
226
227.. code-block:: c++
228
229  #include "chat_protos/chat_service.rpc.pb.h"
230
231  namespace {
232
233    using ChatClient = pw_rpc::nanopb::Chat::Client;
234
235    MyChannelOutput output;
236    pw::rpc::Channel channels[] = {pw::rpc::Channel::Create<1>(&output)};
237    pw::rpc::Client client(channels);
238
239    // Callback function for GetRoomInformation.
240    void LogRoomInformation(const RoomInfoResponse& response, Status status);
241
242  }  // namespace
243
244  void InvokeSomeRpcs() {
245    // Instantiate a service client to call Chat service methods on channel 1.
246    ChatClient chat_client(client, 1);
247
248    // The RPC will remain active as long as `call` is alive.
249    auto call = chat_client.GetRoomInformation(
250        {.room = "pigweed"}, LogRoomInformation);
251    if (!call.active()) {
252      // The invocation may fail. This could occur due to an invalid channel ID,
253      // for example. The failure status is forwarded to the to call's
254      // on_rpc_error callback.
255      return;
256    }
257
258    // For simplicity, block until the call completes. An actual implementation
259    // would likely std::move the call somewhere to keep it active while doing
260    // other work.
261    while (call.active()) {
262      Wait();
263    }
264
265    // Do other stuff now that we have the room information.
266  }
267
268Zephyr
269======
270To enable ``pw_rpc.nanopb.*`` for Zephyr add ``CONFIG_PIGWEED_RPC_NANOPB=y`` to
271the project's configuration. This will enable the Kconfig menu for the
272following:
273
274* ``pw_rpc.nanopb.method`` which can be enabled via
275  ``CONFIG_PIGWEED_RPC_NANOPB_METHOD=y``.
276* ``pw_rpc.nanopb.method_union`` which can be enabled via
277  ``CONFIG_PIGWEED_RPC_NANOPB_METHOD_UNION=y``.
278* ``pw_rpc.nanopb.client`` which can be enabled via
279  ``CONFIG_PIGWEED_RPC_NANOPB_CLIENT=y``.
280* ``pw_rpc.nanopb.common`` which can be enabled via
281  ``CONFIG_PIGWEED_RPC_NANOPB_COMMON=y``.
282* ``pw_rpc.nanopb.echo_service`` which can be enabled via
283  ``CONFIG_PIGWEED_RPC_NANOPB_ECHO_SERVICE=y``.
284