blob: 859e466fde620018d685f39f7ef3e33e6ce31ea9 [file] [log] [blame]
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -07001.. _module-pw_rpc_nanopb:
Alexei Frolov7c7a3862020-07-16 15:36:02 -07002
3------
4nanopb
5------
Alexei Frolov3ab26ff2020-07-21 10:44:58 -07006``pw_rpc`` can generate services which encode/decode RPC requests and responses
7as nanopb message structs.
Alexei Frolov7c7a3862020-07-16 15:36:02 -07008
Alexei Frolov3ab26ff2020-07-21 10:44:58 -07009Usage
10=====
Alexei Frolovb499d3f2020-10-28 13:00:08 -070011To enable nanopb code generation, the build argument
12``dir_pw_third_party_nanopb`` must be set to point to a local nanopb
13installation.
Alexei Frolov3ab26ff2020-07-21 10:44:58 -070014
15Define a ``pw_proto_library`` containing the .proto file defining your service
Alexei Frolovb499d3f2020-10-28 13:00:08 -070016(and optionally other related protos), then depend on the ``nanopb_rpc``
Alexei Frolov3ab26ff2020-07-21 10:44:58 -070017version 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 ChatService.
31 pw_source_set("chat_service") {
32 sources = [
33 "chat_service.cc",
34 "chat_service.h",
35 ]
Alexei Frolovb499d3f2020-10-28 13:00:08 -070036 public_deps = [ ":chat_protos.nanopb_rpc" ]
Alexei Frolov3ab26ff2020-07-21 10:44:58 -070037 }
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==================
Alexei Frolovbebba902021-06-09 17:03:52 -070046All examples in this document use the following RPC service definition.
Alexei Frolov3ab26ff2020-07-21 10:44:58 -070047
48.. code:: protobuf
49
50 // chat/chat_protos/chat_service.proto
51
52 syntax = "proto3";
53
54 service ChatService {
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 ``generated`` 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 generated::ChatService<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::ServerContext& ctx,
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::ServerContext& ctx,
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
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800117.. cpp:function:: void ServerWriter::Finish(Status status = OkStatus())
Alexei Frolov3ab26ff2020-07-21 10:44:58 -0700118
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::
131
132 ``pw_rpc`` does not yet support client streaming RPCs.
133
134Bidirectional streaming RPC
135^^^^^^^^^^^^^^^^^^^^^^^^^^^
136.. attention::
137
138 ``pw_rpc`` does not yet support bidirectional streaming RPCs.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700139
140Client-side
141-----------
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700142A corresponding client class is generated for every service defined in the proto
143file. Like the service class, it is placed under the ``generated`` namespace.
144The class is named after the service, with a ``Client`` suffix. For example, the
145``ChatService`` would create a ``generated::ChatServiceClient``.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700146
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700147The client class contains static methods to call each of the service's methods.
Alexei Frolovbebba902021-06-09 17:03:52 -0700148It is not meant to be instantiated.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700149
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700150.. code-block:: c++
151
Alexei Frolovbebba902021-06-09 17:03:52 -0700152 static GetRoomInformationCall GetRoomInformation(
153 Channel& channel,
154 const RoomInfoRequest& request,
155 ::pw::Function<void(Status, const RoomInfoResponse&)> on_response,
156 ::pw::Function<void(Status)> on_rpc_error = nullptr);
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700157
158The ``NanopbClientCall`` object returned by the RPC invocation stores the active
159RPC's context. For more information on ``ClientCall`` objects, refer to the
Alexei Frolovbebba902021-06-09 17:03:52 -0700160:ref:`core RPC documentation <module-pw_rpc-making-calls>`. The type of the
161returned object is complex, so it is aliased using the method name.
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700162
Alexei Frolovbebba902021-06-09 17:03:52 -0700163.. admonition:: Callback invocation
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700164
Alexei Frolovbebba902021-06-09 17:03:52 -0700165 RPC callbacks are invoked synchronously from ``Client::ProcessPacket``.
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700166
Alexei Frolovbebba902021-06-09 17:03:52 -0700167Method APIs
168^^^^^^^^^^^
169The first argument to each client call method is the channel through which to
170send the RPC. Following that, the arguments depend on the method type.
171
172Unary RPC
173~~~~~~~~~
174A unary RPC call takes the request struct and a callback to invoke when a
175response is received. The callback receives the RPC's status and response
176struct.
177
178An optional second callback can be provided to handle internal errors.
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700179
180.. code-block:: c++
181
Alexei Frolovbebba902021-06-09 17:03:52 -0700182 static GetRoomInformationCall GetRoomInformation(
183 Channel& channel,
184 const RoomInfoRequest& request,
185 ::pw::Function<void(const RoomInfoResponse&, Status)> on_response,
186 ::pw::Function<void(Status)> on_rpc_error = nullptr);
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700187
Alexei Frolovbebba902021-06-09 17:03:52 -0700188Server streaming RPC
189~~~~~~~~~~~~~~~~~~~~
190A server streaming RPC call takes the initial request struct and two callbacks.
191The first is invoked on every stream response received, and the second is
192invoked once the stream is complete with its overall status.
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700193
Alexei Frolovbebba902021-06-09 17:03:52 -0700194An optional third callback can be provided to handle internal errors.
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700195
196.. code-block:: c++
197
Alexei Frolovbebba902021-06-09 17:03:52 -0700198 static ListUsersInRoomCall ListUsersInRoom(
199 Channel& channel,
200 const ListUsersRequest& request,
201 ::pw::Function<void(const ListUsersResponse&)> on_response,
202 ::pw::Function<void(Status)> on_stream_end,
203 ::pw::Function<void(Status)> on_rpc_error = nullptr);
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700204
205Example usage
Alexei Frolovbebba902021-06-09 17:03:52 -0700206^^^^^^^^^^^^^
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700207The following example demonstrates how to call an RPC method using a nanopb
208service client and receive the response.
209
210.. code-block:: c++
211
212 #include "chat_protos/chat_service.rpc.pb.h"
213
214 namespace {
215 MyChannelOutput output;
216 pw::rpc::Channel channels[] = {pw::rpc::Channel::Create<0>(&output)};
217 pw::rpc::Client client(channels);
Alexei Frolovbebba902021-06-09 17:03:52 -0700218
219 // Callback function for GetRoomInformation.
220 void LogRoomInformation(const RoomInfoResponse& response, Status status);
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700221 }
222
223 void InvokeSomeRpcs() {
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700224 // The RPC will remain active as long as `call` is alive.
225 auto call = ChatServiceClient::GetRoomInformation(channels[0],
226 {.room = "pigweed"},
Alexei Frolovbebba902021-06-09 17:03:52 -0700227 LogRoomInformation);
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700228
229 // For simplicity, block here. An actual implementation would likely
230 // std::move the call somewhere to keep it active while doing other work.
231 while (call.active()) {
232 Wait();
233 }
234
Alexei Frolovbebba902021-06-09 17:03:52 -0700235 // Do other stuff now that we have the room information.
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700236 }