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