blob: 47eb2353b1ca6adc460367fb0fe3f4b26b921618 [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 Hepler455b4922020-09-18 00:19:21 -07009.. admonition:: Try it out!
10
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -070011 For a quick intro to ``pw_rpc``, see the
12 :ref:`module-pw_hdlc_lite-rpc-example` in the :ref:`module-pw_hdlc_lite`
13 module.
Wyatt Hepler455b4922020-09-18 00:19:21 -070014
Wyatt Hepler067dd7e2020-07-14 19:34:32 -070015.. attention::
Alexei Frolov26e3ae62020-05-04 17:06:17 -070016
Wyatt Hepler455b4922020-09-18 00:19:21 -070017 This documentation is under construction.
18
19Creating an RPC
20===============
21
221. RPC service declaration
23--------------------------
24Pigweed RPCs are declared in a protocol buffer service definition.
25
26* `Protocol Buffer service documentation
27 <https://developers.google.com/protocol-buffers/docs/proto3#services>`_
28* `gRPC service definition documentation
29 <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_
30
31.. code-block:: protobuf
32
33 syntax = "proto3";
34
35 package foo.bar;
36
37 message Request {}
38
39 message Response {
40 int32 number = 1;
41 }
42
43 service TheService {
44 rpc MethodOne(Request) returns (Response) {}
45 rpc MethodTwo(Request) returns (stream Response) {}
46 }
47
48This protocol buffer is declared in a ``BUILD.gn`` file as follows:
49
50.. code-block:: python
51
52 import("//build_overrides/pigweed.gni")
53 import("$dir_pw_protobuf_compiler/proto.gni")
54
55 pw_proto_library("the_service_proto") {
56 sources = [ "foo_bar/the_service.proto" ]
57 }
58
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800592. RPC code generation
60----------------------
61``pw_rpc`` generates a C++ header file for each ``.proto`` file. This header is
62generated in the build output directory. Its exact location varies by build
63system and toolchain, but the C++ include path always matches the sources
64declaration in the ``pw_proto_library``. The ``.proto`` extension is replaced
65with an extension corresponding to the protobuf library in use.
Wyatt Hepler455b4922020-09-18 00:19:21 -070066
Wyatt Hepler8779bcd2020-11-25 07:25:16 -080067================== =============== =============== =============
68Protobuf libraries Build subtarget Protobuf header pw_rpc header
69================== =============== =============== =============
70Raw only .raw_rpc (none) .raw_rpc.pb.h
71Nanopb or raw .nanopb_rpc .pb.h .rpc.pb.h
72pw_protobuf or raw .pwpb_rpc .pwpb.h .rpc.pwpb.h
73================== =============== =============== =============
74
75For example, the generated RPC header for ``"foo_bar/the_service.proto"`` is
76``"foo_bar/the_service.rpc.pb.h"`` for Nanopb or
77``"foo_bar/the_service.raw_rpc.pb.h"`` for raw RPCs.
78
79The generated header defines a base class for each RPC service declared in the
80``.proto`` file. A service named ``TheService`` in package ``foo.bar`` would
81generate the following base class:
Wyatt Hepler455b4922020-09-18 00:19:21 -070082
83.. cpp:class:: template <typename Implementation> foo::bar::generated::TheService
84
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800853. RPC service definition
86-------------------------
87The serivce class is implemented by inheriting from the generated RPC service
88base class and defining a method for each RPC. The methods must match the name
89and function signature for one of the supported protobuf implementations.
90Services may mix and match protobuf implementations within one service.
91
92.. tip::
93
94 The generated code includes RPC service implementation stubs. You can
95 reference or copy and paste these to get started with implementing a service.
96 These stub classes are generated at the bottom of the pw_rpc proto header.
97
Wyatt Hepler455b4922020-09-18 00:19:21 -070098A Nanopb implementation of this service would be as follows:
99
100.. code-block:: cpp
101
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800102 #include "foo_bar/the_service.rpc.pb.h"
103
Wyatt Hepler455b4922020-09-18 00:19:21 -0700104 namespace foo::bar {
105
106 class TheService : public generated::TheService<TheService> {
107 public:
108 pw::Status MethodOne(ServerContext& ctx,
109 const foo_bar_Request& request,
110 foo_bar_Response& response) {
111 // implementation
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800112 return pw::Status::Ok();
Wyatt Hepler455b4922020-09-18 00:19:21 -0700113 }
114
115 void MethodTwo(ServerContext& ctx,
116 const foo_bar_Request& request,
117 ServerWriter<foo_bar_Response>& response) {
118 // implementation
119 response.Write(foo_bar_Response{.number = 123});
120 }
121 };
122
123 } // namespace foo::bar
124
125The Nanopb implementation would be declared in a ``BUILD.gn``:
126
127.. code-block:: python
128
129 import("//build_overrides/pigweed.gni")
130
131 import("$dir_pw_build/target_types.gni")
132
133 pw_source_set("the_service") {
134 public_configs = [ ":public" ]
135 public = [ "public/foo_bar/service.h" ]
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800136 public_deps = [ ":the_service_proto.nanopb_rpc" ]
Wyatt Hepler455b4922020-09-18 00:19:21 -0700137 }
138
139.. attention::
140
141 pw_rpc's generated classes will support using ``pw_protobuf`` or raw buffers
142 (no protobuf library) in the future.
143
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08001444. Register the service with a server
Wyatt Hepler455b4922020-09-18 00:19:21 -0700145-------------------------------------
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700146This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc_lite>`
147channel output and the example service.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700148
149.. code-block:: cpp
150
151 // Set up the output channel for the pw_rpc server to use. This configures the
152 // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
153 // adapt this as necessary.
154 pw::stream::SysIoWriter writer;
155 pw::rpc::RpcChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
156 writer, pw::hdlc_lite::kDefaultRpcAddress, "HDLC output");
157
158 pw::rpc::Channel channels[] = {
159 pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
160
161 // Declare the pw_rpc server with the HDLC channel.
162 pw::rpc::Server server(channels);
163
164 pw::rpc::TheService the_service;
165
166 void RegisterServices() {
167 // Register the foo.bar.TheService example service.
168 server.Register(the_service);
169
170 // Register other services
171 }
172
173 int main() {
174 // Set up the server.
175 RegisterServices();
176
177 // Declare a buffer for decoding incoming HDLC frames.
178 std::array<std::byte, kMaxTransmissionUnit> input_buffer;
179
180 PW_LOG_INFO("Starting pw_rpc server");
181 pw::hdlc_lite::ReadAndProcessPackets(
182 server, hdlc_channel_output, input_buffer);
183 }
Wyatt Hepler948f5472020-06-02 16:52:28 -0700184
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700185Services
186========
187A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc``
188uses these .proto definitions to generate code for a base service, from which
189user-defined RPCs are implemented.
190
191``pw_rpc`` supports multiple protobuf libraries, and the generated code API
192depends on which is used.
193
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700194.. _module-pw_rpc-protobuf-library-apis:
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700195
196Protobuf library APIs
197=====================
198
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700199.. toctree::
200 :maxdepth: 1
201
202 nanopb/docs
203
204Testing a pw_rpc integration
205============================
206After setting up a ``pw_rpc`` server in your project, you can test that it is
207working as intended by registering the provided ``EchoService``, defined in
208``pw_rpc_protos/echo.proto``, which echoes back a message that it receives.
209
210.. literalinclude:: pw_rpc_protos/echo.proto
211 :language: protobuf
212 :lines: 14-
213
214For example, in C++ with nanopb:
215
216.. code:: c++
217
218 #include "pw_rpc/server.h"
219
220 // Include the apporpriate header for your protobuf library.
221 #include "pw_rpc/echo_service_nanopb.h"
222
223 constexpr pw::rpc::Channel kChannels[] = { /* ... */ };
224 static pw::rpc::Server server(kChannels);
225
226 static pw::rpc::EchoService echo_service;
227
228 void Init() {
229 server.RegisterService(&echo_service);
230 }
231
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700232Protocol description
233====================
234Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These
235packets are used to send requests and responses, control streams, cancel ongoing
236RPCs, and report errors.
237
238Packet format
239-------------
240Pigweed RPC packets consist of a type and a set of fields. The packets are
241encoded as protocol buffers. The full packet format is described in
242``pw_rpc/pw_rpc_protos/packet.proto``.
243
244.. literalinclude:: pw_rpc_protos/packet.proto
245 :language: protobuf
246 :lines: 14-
247
248The packet type and RPC type determine which fields are present in a Pigweed RPC
Wyatt Hepler0f262352020-07-29 09:51:27 -0700249packet. Each packet type is only sent by either the client or the server.
250These tables describe the meaning of and fields included with each packet type.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700251
Wyatt Hepler0f262352020-07-29 09:51:27 -0700252Client-to-server packets
253^^^^^^^^^^^^^^^^^^^^^^^^
254+---------------------------+----------------------------------+
255| packet type | description |
256+===========================+==================================+
257| REQUEST | RPC request |
258| | |
259| | .. code-block:: text |
260| | |
261| | - channel_id |
262| | - service_id |
263| | - method_id |
264| | - payload |
265| | (unless first client stream) |
266| | |
267+---------------------------+----------------------------------+
268| CLIENT_STREAM_END | Client stream finished |
269| | |
270| | .. code-block:: text |
271| | |
272| | - channel_id |
273| | - service_id |
274| | - method_id |
275| | |
276| | |
277+---------------------------+----------------------------------+
278| CLIENT_ERROR | Received unexpected packet |
279| | |
280| | .. code-block:: text |
281| | |
282| | - channel_id |
283| | - service_id |
284| | - method_id |
285| | - status |
286+---------------------------+----------------------------------+
287| CANCEL_SERVER_STREAM | Cancel a server stream |
288| | |
289| | .. code-block:: text |
290| | |
291| | - channel_id |
292| | - service_id |
293| | - method_id |
294| | |
295+---------------------------+----------------------------------+
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700296
Wyatt Hepler0f262352020-07-29 09:51:27 -0700297**Errors**
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700298
Wyatt Hepler0f262352020-07-29 09:51:27 -0700299The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet
300it did not request. If the RPC is a streaming RPC, the server should abort it.
301
302The status code indicates the type of error. If the client does not distinguish
303between the error types, it can send whichever status is most relevant. The
304status code is logged, but all status codes result in the same action by the
305server: aborting the RPC.
306
307* ``NOT_FOUND`` -- Received a packet for a service method the client does not
308 recognize.
309* ``FAILED_PRECONDITION`` -- Received a packet for a service method that the
310 client did not invoke.
311
312Server-to-client packets
313^^^^^^^^^^^^^^^^^^^^^^^^
314+-------------------+--------------------------------+
315| packet type | description |
316+===================+================================+
317| RESPONSE | RPC response |
318| | |
319| | .. code-block:: text |
320| | |
321| | - channel_id |
322| | - service_id |
323| | - method_id |
324| | - payload |
325| | - status |
326| | (unless in server stream) |
327+-------------------+--------------------------------+
328| SERVER_STREAM_END | Server stream and RPC finished |
329| | |
330| | .. code-block:: text |
331| | |
332| | - channel_id |
333| | - service_id |
334| | - method_id |
335| | - status |
336+-------------------+--------------------------------+
337| SERVER_ERROR | Received unexpected packet |
338| | |
339| | .. code-block:: text |
340| | |
341| | - channel_id |
342| | - service_id (if relevant) |
343| | - method_id (if relevant) |
344| | - status |
345+-------------------+--------------------------------+
346
347**Errors**
348
349The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot
350process. The client should abort any RPC for which it receives an error. The
351status field indicates the type of error.
352
353* ``NOT_FOUND`` -- The requested service or method does not exist.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700354* ``FAILED_PRECONDITION`` -- Attempted to cancel an RPC that is not pending.
355* ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel
356 could not be allocated for it.
Wyatt Hepler712d3672020-07-13 15:52:11 -0700357* ``INTERNAL`` -- The server was unable to respond to an RPC due to an
358 unrecoverable internal error.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700359
360Inovking a service method
361-------------------------
362Calling an RPC requires a specific sequence of packets. This section describes
363the protocol for calling service methods of each type: unary, server streaming,
364client streaming, and bidirectional streaming.
365
366Unary RPC
367^^^^^^^^^
368In a unary RPC, the client sends a single request and the server sends a single
369response.
370
371.. seqdiag::
372 :scale: 110
373
374 seqdiag {
375 default_note_color = aliceblue;
376
377 client -> server [
378 label = "request",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700379 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700380 ];
381
382 client <- server [
383 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700384 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700385 ];
386 }
387
388Server streaming RPC
389^^^^^^^^^^^^^^^^^^^^
390In a server streaming RPC, the client sends a single request and the server
Wyatt Hepler0f262352020-07-29 09:51:27 -0700391sends any number of responses followed by a ``SERVER_STREAM_END`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700392
393.. seqdiag::
394 :scale: 110
395
396 seqdiag {
397 default_note_color = aliceblue;
398
399 client -> server [
400 label = "request",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700401 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700402 ];
403
404 client <-- server [
405 noactivate,
406 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700407 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700408 ];
409
410 client <- server [
411 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700412 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700413 ];
414 }
415
416Server streaming RPCs may be cancelled by the client. The client sends a
Wyatt Hepler0f262352020-07-29 09:51:27 -0700417``CANCEL_SERVER_STREAM`` packet to terminate the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700418
419.. seqdiag::
420 :scale: 110
421
422 seqdiag {
423 default_note_color = aliceblue;
424
425 client -> server [
426 label = "request",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700427 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700428 ];
429
430 client <-- server [
431 noactivate,
432 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700433 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700434 ];
435
436 client -> server [
437 noactivate,
438 label = "cancel",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700439 leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700440 ];
441
442 client <- server [
443 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700444 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700445 ];
446 }
447
448Client streaming RPC
449^^^^^^^^^^^^^^^^^^^^
450In a client streaming RPC, the client sends any number of RPC requests followed
Wyatt Hepler0f262352020-07-29 09:51:27 -0700451by a ``CLIENT_STREAM_END`` packet. The server then sends a single response.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700452
453The first client-to-server RPC packet does not include a payload.
454
455.. attention::
456
457 ``pw_rpc`` does not yet support client streaming RPCs.
458
459.. seqdiag::
460 :scale: 110
461
462 seqdiag {
463 default_note_color = aliceblue;
464
465 client -> server [
466 label = "start",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700467 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700468 ];
469
470 client --> server [
471 noactivate,
472 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700473 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700474 ];
475
476 client -> server [
477 noactivate,
478 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700479 leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700480 ];
481
482 client <- server [
483 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700484 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700485 ];
486 }
487
488The server may terminate a client streaming RPC at any time by sending its
489response packet.
490
491.. seqdiag::
492 :scale: 110
493
494 seqdiag {
495 default_note_color = aliceblue;
496
497 client -> server [
498 label = "start",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700499 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700500 ];
501
502 client --> server [
503 noactivate,
504 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700505 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700506 ];
507
508 client <- server [
509 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700510 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700511 ];
512 }
513
514Bidirectional streaming RPC
515^^^^^^^^^^^^^^^^^^^^^^^^^^^
516In a bidirectional streaming RPC, the client sends any number of requests and
Wyatt Hepler0f262352020-07-29 09:51:27 -0700517the server sends any number of responses. The client sends a
518``CLIENT_STREAM_END`` packet when it has finished sending requests. The server
519sends a ``SERVER_STREAM_END`` packet after it receives the client's
520``CLIENT_STREAM_END`` and finished sending its responses.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700521
522The first client-to-server RPC packet does not include a payload.
523
524.. attention::
525
526 ``pw_rpc`` does not yet support bidirectional streaming RPCs.
527
528.. seqdiag::
529 :scale: 110
530
531 seqdiag {
532 default_note_color = aliceblue;
533
534 client -> server [
535 label = "start",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700536 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700537 ];
538
539 client --> server [
540 noactivate,
541 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700542 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700543 ];
544
545 ... (messages in any order) ...
546
547 client <-- server [
548 noactivate,
549 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700550 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700551 ];
552
553 client -> server [
554 noactivate,
555 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700556 leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700557 ];
558
559 client <-- server [
560 noactivate,
561 label = "responses (zero or more)",
562 rightnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
563 ];
564
565 client <- server [
566 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700567 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700568 ];
569 }
570
Wyatt Hepler0f262352020-07-29 09:51:27 -0700571The server may terminate the RPC at any time by sending a ``SERVER_STREAM_END``
572packet with the status, even if the client has not sent its ``STREAM_END``. The
573client may cancel the RPC at any time by sending a ``CANCEL_SERVER_STREAM``
574packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700575
576.. seqdiag::
577 :scale: 110
578
579 seqdiag {
580 default_note_color = aliceblue;
581
582 client -> server [
583 label = "start",
584 leftnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID"
585 ];
586
587 client --> server [
588 noactivate,
589 label = "requests (zero or more)",
590 leftnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
591 ];
592
593 client <-- server [
594 noactivate,
595 label = "responses (zero or more)",
596 rightnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
597 ];
598
599 client -> server [
600 noactivate,
601 label = "cancel",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700602 leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700603 ];
604
605 client <- server [
606 label = "done",
607 rightnote = "PacketType.STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
608 ];
609 }
610
Wyatt Hepler948f5472020-06-02 16:52:28 -0700611RPC server
612==========
613Declare an instance of ``rpc::Server`` and register services with it.
614
615.. admonition:: TODO
616
617 Document the public interface
618
Alexei Frolovbf33d212020-09-15 17:13:45 -0700619Size report
620-----------
621The following size report showcases the memory usage of the core RPC server. It
622is configured with a single channel using a basic transport interface that
623directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
624packet buffer, which comprises the plurality of the example's RAM usage. This is
625not a suitable transport for an actual product; a real implementation would have
626additional overhead proportional to the complexity of the transport.
627
628.. include:: server_size
629
Wyatt Hepler948f5472020-06-02 16:52:28 -0700630RPC server implementation
631-------------------------
632
633The Method class
634^^^^^^^^^^^^^^^^
635The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
636serves as the bridge between the ``pw_rpc`` server library and the user-defined
Wyatt Heplere95bd722020-11-23 07:49:47 -0800637RPC functions. Each supported protobuf implementation extends ``Method`` to
638implement its request and response proto handling. The ``pw_rpc`` server
639calls into the ``Method`` implementation through the base class's ``Invoke``
640function.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700641
Wyatt Heplere95bd722020-11-23 07:49:47 -0800642``Method`` implementations store metadata about each method, including a
643function pointer to the user-defined method implementation. They also provide
644``static constexpr`` functions for creating each type of method. ``Method``
645implementations must satisfy the ``MethodImplTester`` test class in
646``pw_rpc_private/method_impl_tester.h``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700647
Wyatt Heplere95bd722020-11-23 07:49:47 -0800648See ``pw_rpc/internal/method.h`` for more details about ``Method``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700649
650Packet flow
651^^^^^^^^^^^
652
653Requests
654~~~~~~~~
655
656.. blockdiag::
657
658 blockdiag {
659 packets [shape = beginpoint];
660
661 group {
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700662 label = "pw_rpc library";
Wyatt Hepler948f5472020-06-02 16:52:28 -0700663
664 server [label = "Server"];
Alexei Frolov9a4d6bf2020-08-04 10:33:26 -0700665 service [label = "Service"];
Wyatt Hepler948f5472020-06-02 16:52:28 -0700666 method [label = "internal::Method"];
667 }
668
669 stubs [label = "generated services", shape = ellipse];
670 user [label = "user-defined RPCs", shape = roundedbox];
671
672 packets -> server -> service -> method -> stubs -> user;
673 packets -> server [folded];
674 method -> stubs [folded];
675 }
676
677Responses
678~~~~~~~~~
679
680.. blockdiag::
681
682 blockdiag {
683 user -> stubs [folded];
684
685 group {
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700686 label = "pw_rpc library";
Wyatt Hepler948f5472020-06-02 16:52:28 -0700687
688 server [label = "Server"];
689 method [label = "internal::Method"];
690 channel [label = "Channel"];
691 }
692
693 stubs [label = "generated services", shape = ellipse];
694 user [label = "user-defined RPCs", shape = roundedbox];
695 packets [shape = beginpoint];
696
697 user -> stubs -> method [folded];
698 method -> server -> channel;
699 channel -> packets [folded];
700 }
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700701
702RPC client
703==========
704The RPC client is used to send requests to a server and manages the contexts of
705ongoing RPCs.
706
707Setting up a client
708-------------------
709The ``pw::rpc::Client`` class is instantiated with a list of channels that it
710uses to communicate. These channels can be shared with a server, but multiple
711clients cannot use the same channels.
712
713To send incoming RPC packets from the transport layer to be processed by a
714client, the client's ``ProcessPacket`` function is called with the packet data.
715
716.. code:: c++
717
718 #include "pw_rpc/client.h"
719
720 namespace {
721
722 pw::rpc::Channel my_channels[] = {
723 pw::rpc::Channel::Create<1>(&my_channel_output)};
724 pw::rpc::Client my_client(my_channels);
725
726 } // namespace
727
728 // Called when the transport layer receives an RPC packet.
729 void ProcessRpcPacket(ConstByteSpan packet) {
730 my_client.ProcessPacket(packet);
731 }
732
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700733.. _module-pw_rpc-making-calls:
734
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700735Making RPC calls
736----------------
737RPC calls are not made directly through the client, but using one of its
738registered channels instead. A service client class is generated from a .proto
739file for each selected protobuf library, which is then used to send RPC requests
740through a given channel. The API for this depends on the protobuf library;
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700741please refer to the
742:ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple
743service client implementations can exist simulatenously and share the same
744``Client`` class.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700745
746When a call is made, a ``pw::rpc::ClientCall`` object is returned to the caller.
747This object tracks the ongoing RPC call, and can be used to manage it. An RPC
748call is only active as long as its ``ClientCall`` object is alive.
749
750.. tip::
751 Use ``std::move`` when passing around ``ClientCall`` objects to keep RPCs
752 alive.
753
754Client implementation details
755-----------------------------
756
757The ClientCall class
758^^^^^^^^^^^^^^^^^^^^
759``ClientCall`` stores the context of an active RPC, and serves as the user's
760interface to the RPC client. The core RPC library provides a base ``ClientCall``
761class with common functionality, which is then extended for RPC client
762implementations tied to different protobuf libraries to provide convenient
763interfaces for working with RPCs.
764
765The RPC server stores a list of all of active ``ClientCall`` objects. When an
766incoming packet is recieved, it dispatches to one of its active calls, which
767then decodes the payload and presents it to the user.