blob: 555fc6ab67941663b5f5be9827c19615d146a29a [file] [log] [blame]
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -07001.. _module-pw_rpc:
Alexei Frolov26e3ae62020-05-04 17:06:17 -07002
3------
4pw_rpc
5------
6The ``pw_rpc`` module provides a system for defining and invoking remote
7procedure calls (RPCs) on a device.
8
Wyatt Hepler830d26d2021-02-17 09:07:43 -08009This document discusses the ``pw_rpc`` protocol and its C++ implementation.
10``pw_rpc`` implementations for other languages are described in their own
11documents:
12
13.. toctree::
14 :maxdepth: 1
15
16 py/docs
Jared Weinsteind4649112021-08-19 12:27:23 -070017 ts/docs
Wyatt Hepler830d26d2021-02-17 09:07:43 -080018
Wyatt Hepler455b4922020-09-18 00:19:21 -070019.. admonition:: Try it out!
20
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -070021 For a quick intro to ``pw_rpc``, see the
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080022 :ref:`module-pw_hdlc-rpc-example` in the :ref:`module-pw_hdlc` module.
Wyatt Hepler455b4922020-09-18 00:19:21 -070023
Wyatt Hepler067dd7e2020-07-14 19:34:32 -070024.. attention::
Alexei Frolov26e3ae62020-05-04 17:06:17 -070025
Wyatt Hepler455b4922020-09-18 00:19:21 -070026 This documentation is under construction.
27
Wyatt Hepler85e91402021-08-05 12:48:03 -070028RPC semantics
29=============
30The semantics of ``pw_rpc`` are similar to `gRPC
31<https://grpc.io/docs/what-is-grpc/core-concepts/>`_.
32
33RPC call lifecycle
34------------------
35In ``pw_rpc``, an RPC begins when the client sends a request packet. The server
36receives the request, looks up the relevant service method, then calls into the
37RPC function. The RPC is considered active until the server sends a response
38packet with the RPC's status. The client may terminate an ongoing RPC by
39cancelling it.
40
41``pw_rpc`` supports only one RPC invocation per service/method/channel. If a
42client calls an ongoing RPC on the same channel, the server cancels the ongoing
43call and reinvokes the RPC with the new request. This applies to unary and
44streaming RPCs, though the server may not have an opportunity to cancel a
45synchronously handled unary RPC before it completes. The same RPC may be invoked
46multiple times simultaneously if the invocations are on different channels.
47
Wyatt Hepler5a3a36b2021-09-08 11:15:05 -070048Unrequested responses
49---------------------
50``pw_rpc`` supports sending responses to RPCs that have not yet been invoked by
51a client. This is useful in testing and in situations like an RPC that triggers
52reboot. After the reboot, the device opens the writer object and sends its
53response to the client.
54
55.. admonition:: Under construction
56
57 The ``ReaderWriter::Open()`` API is cumbersome, but a more streamlined API
58 will be added to the generated RPC code.
59
Wyatt Hepler455b4922020-09-18 00:19:21 -070060Creating an RPC
61===============
62
631. RPC service declaration
64--------------------------
65Pigweed RPCs are declared in a protocol buffer service definition.
66
67* `Protocol Buffer service documentation
68 <https://developers.google.com/protocol-buffers/docs/proto3#services>`_
69* `gRPC service definition documentation
70 <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_
71
72.. code-block:: protobuf
73
74 syntax = "proto3";
75
76 package foo.bar;
77
78 message Request {}
79
80 message Response {
81 int32 number = 1;
82 }
83
84 service TheService {
85 rpc MethodOne(Request) returns (Response) {}
86 rpc MethodTwo(Request) returns (stream Response) {}
87 }
88
89This protocol buffer is declared in a ``BUILD.gn`` file as follows:
90
91.. code-block:: python
92
93 import("//build_overrides/pigweed.gni")
94 import("$dir_pw_protobuf_compiler/proto.gni")
95
96 pw_proto_library("the_service_proto") {
97 sources = [ "foo_bar/the_service.proto" ]
98 }
99
Wyatt Hepler830d26d2021-02-17 09:07:43 -0800100.. admonition:: proto2 or proto3 syntax?
101
102 Always use proto3 syntax rather than proto2 for new protocol buffers. Proto2
103 protobufs can be compiled for ``pw_rpc``, but they are not as well supported
104 as proto3. Specifically, ``pw_rpc`` lacks support for non-zero default values
105 in proto2. When using Nanopb with ``pw_rpc``, proto2 response protobufs with
106 non-zero field defaults should be manually initialized to the default struct.
107
108 In the past, proto3 was sometimes avoided because it lacked support for field
109 presence detection. Fortunately, this has been fixed: proto3 now supports
110 ``optional`` fields, which are equivalent to proto2 ``optional`` fields.
111
112 If you need to distinguish between a default-valued field and a missing field,
113 mark the field as ``optional``. The presence of the field can be detected
114 with a ``HasField(name)`` or ``has_<field>`` member, depending on the library.
115
116 Optional fields have some overhead --- default-valued fields are included in
117 the encoded proto, and, if using Nanopb, the proto structs have a
118 ``has_<field>`` flag for each optional field. Use plain fields if field
119 presence detection is not needed.
120
121 .. code-block:: protobuf
122
123 syntax = "proto3";
124
125 message MyMessage {
126 // Leaving this field unset is equivalent to setting it to 0.
127 int32 number = 1;
128
129 // Setting this field to 0 is different from leaving it unset.
130 optional int32 other_number = 2;
131 }
132
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08001332. RPC code generation
134----------------------
135``pw_rpc`` generates a C++ header file for each ``.proto`` file. This header is
136generated in the build output directory. Its exact location varies by build
137system and toolchain, but the C++ include path always matches the sources
138declaration in the ``pw_proto_library``. The ``.proto`` extension is replaced
139with an extension corresponding to the protobuf library in use.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700140
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800141================== =============== =============== =============
142Protobuf libraries Build subtarget Protobuf header pw_rpc header
143================== =============== =============== =============
144Raw only .raw_rpc (none) .raw_rpc.pb.h
145Nanopb or raw .nanopb_rpc .pb.h .rpc.pb.h
146pw_protobuf or raw .pwpb_rpc .pwpb.h .rpc.pwpb.h
147================== =============== =============== =============
148
149For example, the generated RPC header for ``"foo_bar/the_service.proto"`` is
150``"foo_bar/the_service.rpc.pb.h"`` for Nanopb or
151``"foo_bar/the_service.raw_rpc.pb.h"`` for raw RPCs.
152
153The generated header defines a base class for each RPC service declared in the
154``.proto`` file. A service named ``TheService`` in package ``foo.bar`` would
Wyatt Heplerd2496322021-09-10 17:22:14 -0700155generate the following base class for Nanopb:
Wyatt Hepler455b4922020-09-18 00:19:21 -0700156
Wyatt Heplerd2496322021-09-10 17:22:14 -0700157.. cpp:class:: template <typename Implementation> foo::bar::pw_rpc::nanopb::TheService::Service
Wyatt Hepler455b4922020-09-18 00:19:21 -0700158
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08001593. RPC service definition
160-------------------------
161The serivce class is implemented by inheriting from the generated RPC service
162base class and defining a method for each RPC. The methods must match the name
163and function signature for one of the supported protobuf implementations.
164Services may mix and match protobuf implementations within one service.
165
166.. tip::
167
168 The generated code includes RPC service implementation stubs. You can
169 reference or copy and paste these to get started with implementing a service.
170 These stub classes are generated at the bottom of the pw_rpc proto header.
171
Wyatt Heplerdf38ed12021-03-24 08:42:48 -0700172 To use the stubs, do the following:
173
174 #. Locate the generated RPC header in the build directory. For example:
175
176 .. code-block:: sh
177
178 find out/ -name <proto_name>.rpc.pb.h
179
180 #. Scroll to the bottom of the generated RPC header.
181 #. Copy the stub class declaration to a header file.
182 #. Copy the member function definitions to a source file.
183 #. Rename the class or change the namespace, if desired.
184 #. List these files in a build target with a dependency on the
185 ``pw_proto_library``.
186
Wyatt Hepler455b4922020-09-18 00:19:21 -0700187A Nanopb implementation of this service would be as follows:
188
189.. code-block:: cpp
190
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800191 #include "foo_bar/the_service.rpc.pb.h"
192
Wyatt Hepler455b4922020-09-18 00:19:21 -0700193 namespace foo::bar {
194
Wyatt Heplerd2496322021-09-10 17:22:14 -0700195 class TheService : public pw_rpc::nanopb::TheService::Service<TheService> {
Wyatt Hepler455b4922020-09-18 00:19:21 -0700196 public:
Wyatt Heplerebf33b72021-09-09 13:33:56 -0700197 pw::Status MethodOne(ServerContext&,
Wyatt Hepler455b4922020-09-18 00:19:21 -0700198 const foo_bar_Request& request,
199 foo_bar_Response& response) {
200 // implementation
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800201 return pw::OkStatus();
Wyatt Hepler455b4922020-09-18 00:19:21 -0700202 }
203
Wyatt Heplerebf33b72021-09-09 13:33:56 -0700204 void MethodTwo(ServerContext&,
Wyatt Hepler455b4922020-09-18 00:19:21 -0700205 const foo_bar_Request& request,
206 ServerWriter<foo_bar_Response>& response) {
207 // implementation
208 response.Write(foo_bar_Response{.number = 123});
209 }
210 };
211
212 } // namespace foo::bar
213
214The Nanopb implementation would be declared in a ``BUILD.gn``:
215
216.. code-block:: python
217
218 import("//build_overrides/pigweed.gni")
219
220 import("$dir_pw_build/target_types.gni")
221
222 pw_source_set("the_service") {
223 public_configs = [ ":public" ]
224 public = [ "public/foo_bar/service.h" ]
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800225 public_deps = [ ":the_service_proto.nanopb_rpc" ]
Wyatt Hepler455b4922020-09-18 00:19:21 -0700226 }
227
228.. attention::
229
230 pw_rpc's generated classes will support using ``pw_protobuf`` or raw buffers
231 (no protobuf library) in the future.
232
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08002334. Register the service with a server
Wyatt Hepler455b4922020-09-18 00:19:21 -0700234-------------------------------------
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800235This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc>`
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700236channel output and the example service.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700237
238.. code-block:: cpp
239
240 // Set up the output channel for the pw_rpc server to use. This configures the
241 // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
242 // adapt this as necessary.
243 pw::stream::SysIoWriter writer;
244 pw::rpc::RpcChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800245 writer, pw::hdlc::kDefaultRpcAddress, "HDLC output");
Wyatt Hepler455b4922020-09-18 00:19:21 -0700246
247 pw::rpc::Channel channels[] = {
248 pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
249
250 // Declare the pw_rpc server with the HDLC channel.
251 pw::rpc::Server server(channels);
252
253 pw::rpc::TheService the_service;
254
255 void RegisterServices() {
256 // Register the foo.bar.TheService example service.
257 server.Register(the_service);
258
259 // Register other services
260 }
261
262 int main() {
263 // Set up the server.
264 RegisterServices();
265
266 // Declare a buffer for decoding incoming HDLC frames.
267 std::array<std::byte, kMaxTransmissionUnit> input_buffer;
268
269 PW_LOG_INFO("Starting pw_rpc server");
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800270 pw::hdlc::ReadAndProcessPackets(
Wyatt Hepler455b4922020-09-18 00:19:21 -0700271 server, hdlc_channel_output, input_buffer);
272 }
Wyatt Hepler948f5472020-06-02 16:52:28 -0700273
Alexei Frolov1c670a22021-04-09 10:18:17 -0700274Channels
275========
276``pw_rpc`` sends all of its packets over channels. These are logical,
277application-layer routes used to tell the RPC system where a packet should go.
278
279Channels over a client-server connection must all have a unique ID, which can be
280assigned statically at compile time or dynamically.
281
282.. code-block:: cpp
283
284 // Creating a channel with the static ID 3.
285 pw::rpc::Channel static_channel = pw::rpc::Channel::Create<3>(&output);
286
287 // Grouping channel IDs within an enum can lead to clearer code.
288 enum ChannelId {
289 kUartChannel = 1,
290 kSpiChannel = 2,
291 };
292
293 // Creating a channel with a static ID defined within an enum.
294 pw::rpc::Channel another_static_channel =
295 pw::rpc::Channel::Create<ChannelId::kUartChannel>(&output);
296
297 // Creating a channel with a dynamic ID (note that no output is provided; it
298 // will be set when the channel is used.
299 pw::rpc::Channel dynamic_channel;
300
Alexei Frolov567e6702021-04-13 09:13:02 -0700301Sometimes, the ID and output of a channel are not known at compile time as they
302depend on information stored on the physical device. To support this use case, a
303dynamically-assignable channel can be configured once at runtime with an ID and
304output.
305
306.. code-block:: cpp
307
308 // Create a dynamic channel without a compile-time ID or output.
309 pw::rpc::Channel dynamic_channel;
310
311 void Init() {
312 // Called during boot to pull the channel configuration from the system.
313 dynamic_channel.Configure(GetChannelId(), some_output);
314 }
315
316
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700317Services
318========
319A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc``
320uses these .proto definitions to generate code for a base service, from which
321user-defined RPCs are implemented.
322
323``pw_rpc`` supports multiple protobuf libraries, and the generated code API
324depends on which is used.
325
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700326.. _module-pw_rpc-protobuf-library-apis:
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700327
328Protobuf library APIs
329=====================
330
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700331.. toctree::
332 :maxdepth: 1
333
334 nanopb/docs
335
336Testing a pw_rpc integration
337============================
338After setting up a ``pw_rpc`` server in your project, you can test that it is
339working as intended by registering the provided ``EchoService``, defined in
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800340``echo.proto``, which echoes back a message that it receives.
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700341
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800342.. literalinclude:: echo.proto
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700343 :language: protobuf
344 :lines: 14-
345
346For example, in C++ with nanopb:
347
348.. code:: c++
349
350 #include "pw_rpc/server.h"
351
352 // Include the apporpriate header for your protobuf library.
353 #include "pw_rpc/echo_service_nanopb.h"
354
355 constexpr pw::rpc::Channel kChannels[] = { /* ... */ };
356 static pw::rpc::Server server(kChannels);
357
358 static pw::rpc::EchoService echo_service;
359
360 void Init() {
Wyatt Hepler31b16ea2021-07-21 08:52:02 -0700361 server.RegisterService(echo_service);
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700362 }
363
Wyatt Hepler31b16ea2021-07-21 08:52:02 -0700364Benchmarking and stress testing
365-------------------------------
366
367.. toctree::
368 :maxdepth: 1
369 :hidden:
370
371 benchmark
372
373``pw_rpc`` provides an RPC service and Python module for stress testing and
374benchmarking a ``pw_rpc`` deployment. See :ref:`module-pw_rpc-benchmark`.
375
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700376Protocol description
377====================
378Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These
379packets are used to send requests and responses, control streams, cancel ongoing
380RPCs, and report errors.
381
382Packet format
383-------------
384Pigweed RPC packets consist of a type and a set of fields. The packets are
385encoded as protocol buffers. The full packet format is described in
Wyatt Heplerba325e42021-03-08 14:23:34 -0800386``pw_rpc/pw_rpc/internal/packet.proto``.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700387
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800388.. literalinclude:: internal/packet.proto
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700389 :language: protobuf
390 :lines: 14-
391
392The packet type and RPC type determine which fields are present in a Pigweed RPC
Wyatt Hepler0f262352020-07-29 09:51:27 -0700393packet. Each packet type is only sent by either the client or the server.
394These tables describe the meaning of and fields included with each packet type.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700395
Wyatt Hepler0f262352020-07-29 09:51:27 -0700396Client-to-server packets
397^^^^^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700398+-------------------+-------------------------------------+
399| packet type | description |
400+===================+=====================================+
401| REQUEST | Invoke an RPC |
402| | |
403| | .. code-block:: text |
404| | |
405| | - channel_id |
406| | - service_id |
407| | - method_id |
408| | - payload |
409| | (unary & server streaming only) |
410| | |
411+-------------------+-------------------------------------+
412| CLIENT_STREAM | Message in a client stream |
413| | |
414| | .. code-block:: text |
415| | |
416| | - channel_id |
417| | - service_id |
418| | - method_id |
419| | - payload |
420| | |
421+-------------------+-------------------------------------+
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700422| CLIENT_STREAM_END | Client stream is complete |
423| | |
424| | .. code-block:: text |
425| | |
426| | - channel_id |
427| | - service_id |
428| | - method_id |
429| | |
430+-------------------+-------------------------------------+
Wyatt Heplera9211162021-06-12 15:40:11 -0700431| CLIENT_ERROR | Received unexpected packet |
432| | |
433| | .. code-block:: text |
434| | |
435| | - channel_id |
436| | - service_id |
437| | - method_id |
438| | - status |
439| | |
440+-------------------+-------------------------------------+
441| CANCEL | Cancel an ongoing RPC |
442| | |
443| | .. code-block:: text |
444| | |
445| | - channel_id |
446| | - service_id |
447| | - method_id |
448| | |
449+-------------------+-------------------------------------+
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700450
Wyatt Hepler0f262352020-07-29 09:51:27 -0700451**Errors**
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700452
Wyatt Hepler0f262352020-07-29 09:51:27 -0700453The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet
454it did not request. If the RPC is a streaming RPC, the server should abort it.
455
Wyatt Heplera9211162021-06-12 15:40:11 -0700456The status code indicates the type of error. The status code is logged, but all
457status codes result in the same action by the server: aborting the RPC.
Wyatt Hepler0f262352020-07-29 09:51:27 -0700458
459* ``NOT_FOUND`` -- Received a packet for a service method the client does not
460 recognize.
461* ``FAILED_PRECONDITION`` -- Received a packet for a service method that the
462 client did not invoke.
Wyatt Hepler35240da2021-07-21 08:51:22 -0700463* ``DATA_LOSS`` -- Received a corrupt packet for a pending service method.
Wyatt Hepler0f262352020-07-29 09:51:27 -0700464
465Server-to-client packets
466^^^^^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700467+-------------------+-------------------------------------+
468| packet type | description |
469+===================+=====================================+
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700470| RESPONSE | The RPC is complete |
471| | |
472| | .. code-block:: text |
473| | |
474| | - channel_id |
475| | - service_id |
476| | - method_id |
477| | - status |
478| | - payload |
479| | (unary & client streaming only) |
480| | |
481+-------------------+-------------------------------------+
482| SERVER_STREAM | Message in a server stream |
Wyatt Heplera9211162021-06-12 15:40:11 -0700483| | |
484| | .. code-block:: text |
485| | |
486| | - channel_id |
487| | - service_id |
488| | - method_id |
489| | - payload |
Wyatt Heplera9211162021-06-12 15:40:11 -0700490| | |
491+-------------------+-------------------------------------+
492| SERVER_ERROR | Received unexpected packet |
493| | |
494| | .. code-block:: text |
495| | |
496| | - channel_id |
497| | - service_id (if relevant) |
498| | - method_id (if relevant) |
499| | - status |
500| | |
501+-------------------+-------------------------------------+
Wyatt Hepler0f262352020-07-29 09:51:27 -0700502
503**Errors**
504
505The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot
506process. The client should abort any RPC for which it receives an error. The
507status field indicates the type of error.
508
509* ``NOT_FOUND`` -- The requested service or method does not exist.
Wyatt Heplera9211162021-06-12 15:40:11 -0700510* ``FAILED_PRECONDITION`` -- A client stream or cancel packet was sent for an
511 RPC that is not pending.
Wyatt Hepler01fc15b2021-06-10 18:15:59 -0700512* ``INVALID_ARGUMENT`` -- The client sent a packet type to an RPC that does not
513 support it (e.g. a ``CLIENT_STREAM`` was sent to a server streaming RPC).
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700514* ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel
515 could not be allocated for it.
Wyatt Hepler712d3672020-07-13 15:52:11 -0700516* ``INTERNAL`` -- The server was unable to respond to an RPC due to an
517 unrecoverable internal error.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700518
519Inovking a service method
520-------------------------
521Calling an RPC requires a specific sequence of packets. This section describes
522the protocol for calling service methods of each type: unary, server streaming,
523client streaming, and bidirectional streaming.
524
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700525The basic flow for all RPC invocations is as follows:
526
527 * Client sends a ``REQUEST`` packet. Includes a payload for unary & server
528 streaming RPCs.
529 * For client and bidirectional streaming RPCs, the client may send any number
530 of ``CLIENT_STREAM`` packets with payloads.
531 * For server and bidirectional streaming RPCs, the server may send any number
532 of ``SERVER_STREAM`` packets.
533 * The server sends a ``RESPONSE`` packet. Includes a payload for unary &
534 client streaming RPCs. The RPC is complete.
535
536The client may cancel an ongoing RPC at any time by sending a ``CANCEL`` packet.
537The server may finish an ongoing RPC at any time by sending the ``RESPONSE``
538packet.
539
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700540Unary RPC
541^^^^^^^^^
542In a unary RPC, the client sends a single request and the server sends a single
543response.
544
Wyatt Hepler1d221242021-09-07 15:42:21 -0700545.. image:: unary_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700546
Wyatt Heplera9211162021-06-12 15:40:11 -0700547The client may attempt to cancel a unary RPC by sending a ``CANCEL`` packet. The
548server sends no response to a cancelled RPC. If the server processes the unary
549RPC synchronously (the handling thread sends the response), it may not be
550possible to cancel the RPC.
551
Wyatt Hepler1d221242021-09-07 15:42:21 -0700552.. image:: unary_rpc_cancelled.svg
Wyatt Heplera9211162021-06-12 15:40:11 -0700553
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700554Server streaming RPC
555^^^^^^^^^^^^^^^^^^^^
556In a server streaming RPC, the client sends a single request and the server
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700557sends any number of ``SERVER_STREAM`` packets followed by a ``RESPONSE`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700558
Wyatt Hepler1d221242021-09-07 15:42:21 -0700559.. image:: server_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700560
Wyatt Heplera9211162021-06-12 15:40:11 -0700561The client may terminate a server streaming RPC by sending a ``CANCEL`` packet.
562The server sends no response.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700563
Wyatt Hepler1d221242021-09-07 15:42:21 -0700564.. image:: server_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700565
566Client streaming RPC
567^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700568In a client streaming RPC, the client starts the RPC by sending a ``REQUEST``
569packet with no payload. It then sends any number of messages in
570``CLIENT_STREAM`` packets, followed by a ``CLIENT_STREAM_END``. The server sends
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700571a single ``RESPONSE`` to finish the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700572
Wyatt Hepler1d221242021-09-07 15:42:21 -0700573.. image:: client_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700574
Wyatt Heplera9211162021-06-12 15:40:11 -0700575The server may finish the RPC at any time by sending its ``RESPONSE`` packet,
576even if it has not yet received the ``CLIENT_STREAM_END`` packet. The client may
577terminate the RPC at any time by sending a ``CANCEL`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700578
Wyatt Hepler1d221242021-09-07 15:42:21 -0700579.. image:: client_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700580
581Bidirectional streaming RPC
582^^^^^^^^^^^^^^^^^^^^^^^^^^^
583In a bidirectional streaming RPC, the client sends any number of requests and
Wyatt Heplera9211162021-06-12 15:40:11 -0700584the server sends any number of responses. The client invokes the RPC by sending
585a ``REQUEST`` with no payload. It sends a ``CLIENT_STREAM_END`` packet when it
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700586has finished sending requests. The server sends a ``RESPONSE`` packet to finish
587the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700588
Wyatt Hepler1d221242021-09-07 15:42:21 -0700589.. image:: bidirectional_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700590
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700591The server may finish the RPC at any time by sending the ``RESPONSE`` packet,
592even if it has not received the ``CLIENT_STREAM_END`` packet. The client may
593terminate the RPC at any time by sending a ``CANCEL`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700594
Wyatt Hepler1d221242021-09-07 15:42:21 -0700595.. image:: bidirectional_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700596
Wyatt Hepler948f5472020-06-02 16:52:28 -0700597RPC server
598==========
599Declare an instance of ``rpc::Server`` and register services with it.
600
601.. admonition:: TODO
602
603 Document the public interface
604
Alexei Frolovbf33d212020-09-15 17:13:45 -0700605Size report
606-----------
607The following size report showcases the memory usage of the core RPC server. It
608is configured with a single channel using a basic transport interface that
609directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
610packet buffer, which comprises the plurality of the example's RAM usage. This is
611not a suitable transport for an actual product; a real implementation would have
612additional overhead proportional to the complexity of the transport.
613
614.. include:: server_size
615
Wyatt Hepler948f5472020-06-02 16:52:28 -0700616RPC server implementation
617-------------------------
618
619The Method class
620^^^^^^^^^^^^^^^^
621The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
622serves as the bridge between the ``pw_rpc`` server library and the user-defined
Wyatt Heplere95bd722020-11-23 07:49:47 -0800623RPC functions. Each supported protobuf implementation extends ``Method`` to
624implement its request and response proto handling. The ``pw_rpc`` server
625calls into the ``Method`` implementation through the base class's ``Invoke``
626function.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700627
Wyatt Heplere95bd722020-11-23 07:49:47 -0800628``Method`` implementations store metadata about each method, including a
629function pointer to the user-defined method implementation. They also provide
630``static constexpr`` functions for creating each type of method. ``Method``
631implementations must satisfy the ``MethodImplTester`` test class in
Wyatt Heplerfa6edcc2021-08-20 08:30:08 -0700632``pw_rpc/internal/method_impl_tester.h``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700633
Wyatt Heplere95bd722020-11-23 07:49:47 -0800634See ``pw_rpc/internal/method.h`` for more details about ``Method``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700635
636Packet flow
637^^^^^^^^^^^
638
639Requests
640~~~~~~~~
641
Wyatt Hepler1d221242021-09-07 15:42:21 -0700642.. image:: request_packets.svg
Wyatt Hepler948f5472020-06-02 16:52:28 -0700643
644Responses
645~~~~~~~~~
646
Wyatt Hepler1d221242021-09-07 15:42:21 -0700647.. image:: response_packets.svg
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700648
649RPC client
650==========
651The RPC client is used to send requests to a server and manages the contexts of
652ongoing RPCs.
653
654Setting up a client
655-------------------
656The ``pw::rpc::Client`` class is instantiated with a list of channels that it
657uses to communicate. These channels can be shared with a server, but multiple
658clients cannot use the same channels.
659
660To send incoming RPC packets from the transport layer to be processed by a
661client, the client's ``ProcessPacket`` function is called with the packet data.
662
663.. code:: c++
664
665 #include "pw_rpc/client.h"
666
667 namespace {
668
669 pw::rpc::Channel my_channels[] = {
670 pw::rpc::Channel::Create<1>(&my_channel_output)};
671 pw::rpc::Client my_client(my_channels);
672
673 } // namespace
674
675 // Called when the transport layer receives an RPC packet.
676 void ProcessRpcPacket(ConstByteSpan packet) {
677 my_client.ProcessPacket(packet);
678 }
679
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700680.. _module-pw_rpc-making-calls:
681
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700682Making RPC calls
683----------------
684RPC calls are not made directly through the client, but using one of its
685registered channels instead. A service client class is generated from a .proto
686file for each selected protobuf library, which is then used to send RPC requests
687through a given channel. The API for this depends on the protobuf library;
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700688please refer to the
689:ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple
690service client implementations can exist simulatenously and share the same
691``Client`` class.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700692
693When a call is made, a ``pw::rpc::ClientCall`` object is returned to the caller.
694This object tracks the ongoing RPC call, and can be used to manage it. An RPC
695call is only active as long as its ``ClientCall`` object is alive.
696
697.. tip::
698 Use ``std::move`` when passing around ``ClientCall`` objects to keep RPCs
699 alive.
700
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700701Example
702^^^^^^^
703.. code-block:: c++
704
705 #include "pw_rpc/echo_service_nanopb.h"
706
707 namespace {
Alexei Frolov2b54ee62021-04-29 14:58:21 -0700708 // Generated clients are namespaced with their proto library.
709 using pw::rpc::nanopb::EchoServiceClient;
710
Alexei Frolov73687fb2021-09-03 11:22:33 -0700711 // RPC channel ID on which to make client calls.
712 constexpr uint32_t kDefaultChannelId = 1;
713
Alexei Frolov2b54ee62021-04-29 14:58:21 -0700714 EchoServiceClient::EchoCall echo_call;
Alexei Frolovbebba902021-06-09 17:03:52 -0700715
716 // Callback invoked when a response is received. This is called synchronously
717 // from Client::ProcessPacket.
718 void EchoResponse(const pw_rpc_EchoMessage& response,
719 pw::Status status) {
720 if (status.ok()) {
721 PW_LOG_INFO("Received echo response: %s", response.msg);
722 } else {
723 PW_LOG_ERROR("Echo failed with status %d",
724 static_cast<int>(status.code()));
725 }
726 }
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700727
728 } // namespace
729
730 void CallEcho(const char* message) {
Alexei Frolov73687fb2021-09-03 11:22:33 -0700731 // Create a client to call the EchoService.
732 EchoServiceClient echo_client(my_rpc_client, kDefaultChannelId);
733
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700734 pw_rpc_EchoMessage request = pw_rpc_EchoMessage_init_default;
735 pw::string::Copy(message, request.msg);
736
737 // By assigning the returned ClientCall to the global echo_call, the RPC
738 // call is kept alive until it completes. When a response is received, it
739 // will be logged by the handler function and the call will complete.
Alexei Frolov73687fb2021-09-03 11:22:33 -0700740 echo_call = echo_client.Echo(request, EchoResponse);
741 if (!echo_call.active()) {
742 // The RPC call was not sent. This could occur due to, for example, an
743 // invalid channel ID. Handle if necessary.
744 }
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700745 }
746
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700747Client implementation details
748-----------------------------
749
750The ClientCall class
751^^^^^^^^^^^^^^^^^^^^
752``ClientCall`` stores the context of an active RPC, and serves as the user's
753interface to the RPC client. The core RPC library provides a base ``ClientCall``
754class with common functionality, which is then extended for RPC client
755implementations tied to different protobuf libraries to provide convenient
756interfaces for working with RPCs.
757
758The RPC server stores a list of all of active ``ClientCall`` objects. When an
759incoming packet is recieved, it dispatches to one of its active calls, which
760then decodes the payload and presents it to the user.
Alexei Frolov3e280922021-04-12 14:53:06 -0700761
762ClientServer
763============
764Sometimes, a device needs to both process RPCs as a server, as well as making
765calls to another device as a client. To do this, both a client and server must
766be set up, and incoming packets must be sent to both of them.
767
768Pigweed simplifies this setup by providing a ``ClientServer`` class which wraps
769an RPC client and server with the same set of channels.
770
771.. code-block:: cpp
772
773 pw::rpc::Channel channels[] = {
774 pw::rpc::Channel::Create<1>(&channel_output)};
775
776 // Creates both a client and a server.
777 pw::rpc::ClientServer client_server(channels);
778
779 void ProcessRpcData(pw::ConstByteSpan packet) {
780 // Calls into both the client and the server, sending the packet to the
781 // appropriate one.
782 client_server.ProcessPacket(packet, output);
783 }