blob: 2e62c31c82e500d39ed4855464e9acda4c7a3907 [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
592. RPC service definition
60-------------------------
61``pw_rpc`` generates a C++ base class for each RPC service declared in a .proto
62file. The serivce class is implemented by inheriting from this generated base
63and defining a method for each RPC.
64
65A service named ``TheService`` in package ``foo.bar`` will generate the
66following class:
67
68.. cpp:class:: template <typename Implementation> foo::bar::generated::TheService
69
70A Nanopb implementation of this service would be as follows:
71
72.. code-block:: cpp
73
74 namespace foo::bar {
75
76 class TheService : public generated::TheService<TheService> {
77 public:
78 pw::Status MethodOne(ServerContext& ctx,
79 const foo_bar_Request& request,
80 foo_bar_Response& response) {
81 // implementation
82 return pw::Status::OK;
83 }
84
85 void MethodTwo(ServerContext& ctx,
86 const foo_bar_Request& request,
87 ServerWriter<foo_bar_Response>& response) {
88 // implementation
89 response.Write(foo_bar_Response{.number = 123});
90 }
91 };
92
93 } // namespace foo::bar
94
95The Nanopb implementation would be declared in a ``BUILD.gn``:
96
97.. code-block:: python
98
99 import("//build_overrides/pigweed.gni")
100
101 import("$dir_pw_build/target_types.gni")
102
103 pw_source_set("the_service") {
104 public_configs = [ ":public" ]
105 public = [ "public/foo_bar/service.h" ]
106 public_deps = [ ":the_service_proto_nanopb_rpc" ]
107 }
108
109.. attention::
110
111 pw_rpc's generated classes will support using ``pw_protobuf`` or raw buffers
112 (no protobuf library) in the future.
113
1143. Register the service with a server
115-------------------------------------
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700116This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc_lite>`
117channel output and the example service.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700118
119.. code-block:: cpp
120
121 // Set up the output channel for the pw_rpc server to use. This configures the
122 // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
123 // adapt this as necessary.
124 pw::stream::SysIoWriter writer;
125 pw::rpc::RpcChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
126 writer, pw::hdlc_lite::kDefaultRpcAddress, "HDLC output");
127
128 pw::rpc::Channel channels[] = {
129 pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
130
131 // Declare the pw_rpc server with the HDLC channel.
132 pw::rpc::Server server(channels);
133
134 pw::rpc::TheService the_service;
135
136 void RegisterServices() {
137 // Register the foo.bar.TheService example service.
138 server.Register(the_service);
139
140 // Register other services
141 }
142
143 int main() {
144 // Set up the server.
145 RegisterServices();
146
147 // Declare a buffer for decoding incoming HDLC frames.
148 std::array<std::byte, kMaxTransmissionUnit> input_buffer;
149
150 PW_LOG_INFO("Starting pw_rpc server");
151 pw::hdlc_lite::ReadAndProcessPackets(
152 server, hdlc_channel_output, input_buffer);
153 }
Wyatt Hepler948f5472020-06-02 16:52:28 -0700154
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700155Services
156========
157A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc``
158uses these .proto definitions to generate code for a base service, from which
159user-defined RPCs are implemented.
160
161``pw_rpc`` supports multiple protobuf libraries, and the generated code API
162depends on which is used.
163
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700164.. _module-pw_rpc-protobuf-library-apis:
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700165
166Protobuf library APIs
167=====================
168
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700169.. toctree::
170 :maxdepth: 1
171
172 nanopb/docs
173
174Testing a pw_rpc integration
175============================
176After setting up a ``pw_rpc`` server in your project, you can test that it is
177working as intended by registering the provided ``EchoService``, defined in
178``pw_rpc_protos/echo.proto``, which echoes back a message that it receives.
179
180.. literalinclude:: pw_rpc_protos/echo.proto
181 :language: protobuf
182 :lines: 14-
183
184For example, in C++ with nanopb:
185
186.. code:: c++
187
188 #include "pw_rpc/server.h"
189
190 // Include the apporpriate header for your protobuf library.
191 #include "pw_rpc/echo_service_nanopb.h"
192
193 constexpr pw::rpc::Channel kChannels[] = { /* ... */ };
194 static pw::rpc::Server server(kChannels);
195
196 static pw::rpc::EchoService echo_service;
197
198 void Init() {
199 server.RegisterService(&echo_service);
200 }
201
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700202Protocol description
203====================
204Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These
205packets are used to send requests and responses, control streams, cancel ongoing
206RPCs, and report errors.
207
208Packet format
209-------------
210Pigweed RPC packets consist of a type and a set of fields. The packets are
211encoded as protocol buffers. The full packet format is described in
212``pw_rpc/pw_rpc_protos/packet.proto``.
213
214.. literalinclude:: pw_rpc_protos/packet.proto
215 :language: protobuf
216 :lines: 14-
217
218The packet type and RPC type determine which fields are present in a Pigweed RPC
Wyatt Hepler0f262352020-07-29 09:51:27 -0700219packet. Each packet type is only sent by either the client or the server.
220These tables describe the meaning of and fields included with each packet type.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700221
Wyatt Hepler0f262352020-07-29 09:51:27 -0700222Client-to-server packets
223^^^^^^^^^^^^^^^^^^^^^^^^
224+---------------------------+----------------------------------+
225| packet type | description |
226+===========================+==================================+
227| REQUEST | RPC request |
228| | |
229| | .. code-block:: text |
230| | |
231| | - channel_id |
232| | - service_id |
233| | - method_id |
234| | - payload |
235| | (unless first client stream) |
236| | |
237+---------------------------+----------------------------------+
238| CLIENT_STREAM_END | Client stream finished |
239| | |
240| | .. code-block:: text |
241| | |
242| | - channel_id |
243| | - service_id |
244| | - method_id |
245| | |
246| | |
247+---------------------------+----------------------------------+
248| CLIENT_ERROR | Received unexpected packet |
249| | |
250| | .. code-block:: text |
251| | |
252| | - channel_id |
253| | - service_id |
254| | - method_id |
255| | - status |
256+---------------------------+----------------------------------+
257| CANCEL_SERVER_STREAM | Cancel a server stream |
258| | |
259| | .. code-block:: text |
260| | |
261| | - channel_id |
262| | - service_id |
263| | - method_id |
264| | |
265+---------------------------+----------------------------------+
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700266
Wyatt Hepler0f262352020-07-29 09:51:27 -0700267**Errors**
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700268
Wyatt Hepler0f262352020-07-29 09:51:27 -0700269The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet
270it did not request. If the RPC is a streaming RPC, the server should abort it.
271
272The status code indicates the type of error. If the client does not distinguish
273between the error types, it can send whichever status is most relevant. The
274status code is logged, but all status codes result in the same action by the
275server: aborting the RPC.
276
277* ``NOT_FOUND`` -- Received a packet for a service method the client does not
278 recognize.
279* ``FAILED_PRECONDITION`` -- Received a packet for a service method that the
280 client did not invoke.
281
282Server-to-client packets
283^^^^^^^^^^^^^^^^^^^^^^^^
284+-------------------+--------------------------------+
285| packet type | description |
286+===================+================================+
287| RESPONSE | RPC response |
288| | |
289| | .. code-block:: text |
290| | |
291| | - channel_id |
292| | - service_id |
293| | - method_id |
294| | - payload |
295| | - status |
296| | (unless in server stream) |
297+-------------------+--------------------------------+
298| SERVER_STREAM_END | Server stream and RPC finished |
299| | |
300| | .. code-block:: text |
301| | |
302| | - channel_id |
303| | - service_id |
304| | - method_id |
305| | - status |
306+-------------------+--------------------------------+
307| SERVER_ERROR | Received unexpected packet |
308| | |
309| | .. code-block:: text |
310| | |
311| | - channel_id |
312| | - service_id (if relevant) |
313| | - method_id (if relevant) |
314| | - status |
315+-------------------+--------------------------------+
316
317**Errors**
318
319The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot
320process. The client should abort any RPC for which it receives an error. The
321status field indicates the type of error.
322
323* ``NOT_FOUND`` -- The requested service or method does not exist.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700324* ``FAILED_PRECONDITION`` -- Attempted to cancel an RPC that is not pending.
325* ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel
326 could not be allocated for it.
Wyatt Hepler712d3672020-07-13 15:52:11 -0700327* ``INTERNAL`` -- The server was unable to respond to an RPC due to an
328 unrecoverable internal error.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700329
330Inovking a service method
331-------------------------
332Calling an RPC requires a specific sequence of packets. This section describes
333the protocol for calling service methods of each type: unary, server streaming,
334client streaming, and bidirectional streaming.
335
336Unary RPC
337^^^^^^^^^
338In a unary RPC, the client sends a single request and the server sends a single
339response.
340
341.. seqdiag::
342 :scale: 110
343
344 seqdiag {
345 default_note_color = aliceblue;
346
347 client -> server [
348 label = "request",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700349 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700350 ];
351
352 client <- server [
353 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700354 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700355 ];
356 }
357
358Server streaming RPC
359^^^^^^^^^^^^^^^^^^^^
360In a server streaming RPC, the client sends a single request and the server
Wyatt Hepler0f262352020-07-29 09:51:27 -0700361sends any number of responses followed by a ``SERVER_STREAM_END`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700362
363.. seqdiag::
364 :scale: 110
365
366 seqdiag {
367 default_note_color = aliceblue;
368
369 client -> server [
370 label = "request",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700371 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700372 ];
373
374 client <-- server [
375 noactivate,
376 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700377 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700378 ];
379
380 client <- server [
381 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700382 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700383 ];
384 }
385
386Server streaming RPCs may be cancelled by the client. The client sends a
Wyatt Hepler0f262352020-07-29 09:51:27 -0700387``CANCEL_SERVER_STREAM`` packet to terminate the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700388
389.. seqdiag::
390 :scale: 110
391
392 seqdiag {
393 default_note_color = aliceblue;
394
395 client -> server [
396 label = "request",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700397 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700398 ];
399
400 client <-- server [
401 noactivate,
402 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700403 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700404 ];
405
406 client -> server [
407 noactivate,
408 label = "cancel",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700409 leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700410 ];
411
412 client <- server [
413 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700414 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700415 ];
416 }
417
418Client streaming RPC
419^^^^^^^^^^^^^^^^^^^^
420In a client streaming RPC, the client sends any number of RPC requests followed
Wyatt Hepler0f262352020-07-29 09:51:27 -0700421by a ``CLIENT_STREAM_END`` packet. The server then sends a single response.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700422
423The first client-to-server RPC packet does not include a payload.
424
425.. attention::
426
427 ``pw_rpc`` does not yet support client streaming RPCs.
428
429.. seqdiag::
430 :scale: 110
431
432 seqdiag {
433 default_note_color = aliceblue;
434
435 client -> server [
436 label = "start",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700437 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700438 ];
439
440 client --> server [
441 noactivate,
442 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700443 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700444 ];
445
446 client -> server [
447 noactivate,
448 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700449 leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700450 ];
451
452 client <- server [
453 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700454 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700455 ];
456 }
457
458The server may terminate a client streaming RPC at any time by sending its
459response packet.
460
461.. seqdiag::
462 :scale: 110
463
464 seqdiag {
465 default_note_color = aliceblue;
466
467 client -> server [
468 label = "start",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700469 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700470 ];
471
472 client --> server [
473 noactivate,
474 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700475 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700476 ];
477
478 client <- server [
479 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700480 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700481 ];
482 }
483
484Bidirectional streaming RPC
485^^^^^^^^^^^^^^^^^^^^^^^^^^^
486In a bidirectional streaming RPC, the client sends any number of requests and
Wyatt Hepler0f262352020-07-29 09:51:27 -0700487the server sends any number of responses. The client sends a
488``CLIENT_STREAM_END`` packet when it has finished sending requests. The server
489sends a ``SERVER_STREAM_END`` packet after it receives the client's
490``CLIENT_STREAM_END`` and finished sending its responses.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700491
492The first client-to-server RPC packet does not include a payload.
493
494.. attention::
495
496 ``pw_rpc`` does not yet support bidirectional streaming RPCs.
497
498.. seqdiag::
499 :scale: 110
500
501 seqdiag {
502 default_note_color = aliceblue;
503
504 client -> server [
505 label = "start",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700506 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700507 ];
508
509 client --> server [
510 noactivate,
511 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700512 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700513 ];
514
515 ... (messages in any order) ...
516
517 client <-- server [
518 noactivate,
519 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700520 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700521 ];
522
523 client -> server [
524 noactivate,
525 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700526 leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700527 ];
528
529 client <-- server [
530 noactivate,
531 label = "responses (zero or more)",
532 rightnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
533 ];
534
535 client <- server [
536 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700537 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700538 ];
539 }
540
Wyatt Hepler0f262352020-07-29 09:51:27 -0700541The server may terminate the RPC at any time by sending a ``SERVER_STREAM_END``
542packet with the status, even if the client has not sent its ``STREAM_END``. The
543client may cancel the RPC at any time by sending a ``CANCEL_SERVER_STREAM``
544packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700545
546.. seqdiag::
547 :scale: 110
548
549 seqdiag {
550 default_note_color = aliceblue;
551
552 client -> server [
553 label = "start",
554 leftnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID"
555 ];
556
557 client --> server [
558 noactivate,
559 label = "requests (zero or more)",
560 leftnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
561 ];
562
563 client <-- server [
564 noactivate,
565 label = "responses (zero or more)",
566 rightnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
567 ];
568
569 client -> server [
570 noactivate,
571 label = "cancel",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700572 leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700573 ];
574
575 client <- server [
576 label = "done",
577 rightnote = "PacketType.STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
578 ];
579 }
580
Wyatt Hepler948f5472020-06-02 16:52:28 -0700581RPC server
582==========
583Declare an instance of ``rpc::Server`` and register services with it.
584
585.. admonition:: TODO
586
587 Document the public interface
588
Alexei Frolovbf33d212020-09-15 17:13:45 -0700589Size report
590-----------
591The following size report showcases the memory usage of the core RPC server. It
592is configured with a single channel using a basic transport interface that
593directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
594packet buffer, which comprises the plurality of the example's RAM usage. This is
595not a suitable transport for an actual product; a real implementation would have
596additional overhead proportional to the complexity of the transport.
597
598.. include:: server_size
599
Wyatt Hepler948f5472020-06-02 16:52:28 -0700600RPC server implementation
601-------------------------
602
603The Method class
604^^^^^^^^^^^^^^^^
605The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
606serves as the bridge between the ``pw_rpc`` server library and the user-defined
607RPC functions. ``Method`` takes an RPC packet, decodes it using a protobuf
608library (if applicable), and calls the RPC function. Since ``Method`` interacts
609directly with the protobuf library, it must be implemented separately for each
610protobuf library.
611
612``pw::rpc::internal::Method`` is not implemented as a facade with different
613backends. Instead, there is a separate instance of the ``pw_rpc`` server library
614for each ``Method`` implementation. There are a few reasons for this.
615
616* ``Method`` is entirely internal to ``pw_rpc``. Users will never implement a
617 custom backend. Exposing a facade would unnecessarily expose implementation
618 details and make ``pw_rpc`` more difficult to use.
619* There is no common interface between ``pw_rpc`` / ``Method`` implementations.
620 It's not possible to swap between e.g. a Nanopb and a ``pw_protobuf`` RPC
621 server because the interface for the user-implemented RPCs changes completely.
622 This nullifies the primary benefit of facades.
623* The different ``Method`` implementations can be built easily alongside one
624 another in a cross-platform way. This makes testing simpler, since the tests
625 build with any backend configuration. Users can select which ``Method``
626 implementation to use simply by depending on the corresponding server library.
627
628Packet flow
629^^^^^^^^^^^
630
631Requests
632~~~~~~~~
633
634.. blockdiag::
635
636 blockdiag {
637 packets [shape = beginpoint];
638
639 group {
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700640 label = "pw_rpc library";
Wyatt Hepler948f5472020-06-02 16:52:28 -0700641
642 server [label = "Server"];
Alexei Frolov9a4d6bf2020-08-04 10:33:26 -0700643 service [label = "Service"];
Wyatt Hepler948f5472020-06-02 16:52:28 -0700644 method [label = "internal::Method"];
645 }
646
647 stubs [label = "generated services", shape = ellipse];
648 user [label = "user-defined RPCs", shape = roundedbox];
649
650 packets -> server -> service -> method -> stubs -> user;
651 packets -> server [folded];
652 method -> stubs [folded];
653 }
654
655Responses
656~~~~~~~~~
657
658.. blockdiag::
659
660 blockdiag {
661 user -> stubs [folded];
662
663 group {
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700664 label = "pw_rpc library";
Wyatt Hepler948f5472020-06-02 16:52:28 -0700665
666 server [label = "Server"];
667 method [label = "internal::Method"];
668 channel [label = "Channel"];
669 }
670
671 stubs [label = "generated services", shape = ellipse];
672 user [label = "user-defined RPCs", shape = roundedbox];
673 packets [shape = beginpoint];
674
675 user -> stubs -> method [folded];
676 method -> server -> channel;
677 channel -> packets [folded];
678 }
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700679
680RPC client
681==========
682The RPC client is used to send requests to a server and manages the contexts of
683ongoing RPCs.
684
685Setting up a client
686-------------------
687The ``pw::rpc::Client`` class is instantiated with a list of channels that it
688uses to communicate. These channels can be shared with a server, but multiple
689clients cannot use the same channels.
690
691To send incoming RPC packets from the transport layer to be processed by a
692client, the client's ``ProcessPacket`` function is called with the packet data.
693
694.. code:: c++
695
696 #include "pw_rpc/client.h"
697
698 namespace {
699
700 pw::rpc::Channel my_channels[] = {
701 pw::rpc::Channel::Create<1>(&my_channel_output)};
702 pw::rpc::Client my_client(my_channels);
703
704 } // namespace
705
706 // Called when the transport layer receives an RPC packet.
707 void ProcessRpcPacket(ConstByteSpan packet) {
708 my_client.ProcessPacket(packet);
709 }
710
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700711.. _module-pw_rpc-making-calls:
712
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700713Making RPC calls
714----------------
715RPC calls are not made directly through the client, but using one of its
716registered channels instead. A service client class is generated from a .proto
717file for each selected protobuf library, which is then used to send RPC requests
718through a given channel. The API for this depends on the protobuf library;
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700719please refer to the
720:ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple
721service client implementations can exist simulatenously and share the same
722``Client`` class.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700723
724When a call is made, a ``pw::rpc::ClientCall`` object is returned to the caller.
725This object tracks the ongoing RPC call, and can be used to manage it. An RPC
726call is only active as long as its ``ClientCall`` object is alive.
727
728.. tip::
729 Use ``std::move`` when passing around ``ClientCall`` objects to keep RPCs
730 alive.
731
732Client implementation details
733-----------------------------
734
735The ClientCall class
736^^^^^^^^^^^^^^^^^^^^
737``ClientCall`` stores the context of an active RPC, and serves as the user's
738interface to the RPC client. The core RPC library provides a base ``ClientCall``
739class with common functionality, which is then extended for RPC client
740implementations tied to different protobuf libraries to provide convenient
741interfaces for working with RPCs.
742
743The RPC server stores a list of all of active ``ClientCall`` objects. When an
744incoming packet is recieved, it dispatches to one of its active calls, which
745then decodes the payload and presents it to the user.