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