1.. _module-pw_rpc:
2
3------
4pw_rpc
5------
6The ``pw_rpc`` module provides a system for defining and invoking remote
7procedure calls (RPCs) on a device.
8
9This 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
17
18.. admonition:: Try it out!
19
20  For a quick intro to ``pw_rpc``, see the
21  :ref:`module-pw_hdlc-rpc-example` in the :ref:`module-pw_hdlc` module.
22
23.. attention::
24
25  This documentation is under construction.
26
27Creating an RPC
28===============
29
301. RPC service declaration
31--------------------------
32Pigweed RPCs are declared in a protocol buffer service definition.
33
34* `Protocol Buffer service documentation
35  <https://developers.google.com/protocol-buffers/docs/proto3#services>`_
36* `gRPC service definition documentation
37  <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_
38
39.. code-block:: protobuf
40
41  syntax = "proto3";
42
43  package foo.bar;
44
45  message Request {}
46
47  message Response {
48    int32 number = 1;
49  }
50
51  service TheService {
52    rpc MethodOne(Request) returns (Response) {}
53    rpc MethodTwo(Request) returns (stream Response) {}
54  }
55
56This protocol buffer is declared in a ``BUILD.gn`` file as follows:
57
58.. code-block:: python
59
60  import("//build_overrides/pigweed.gni")
61  import("$dir_pw_protobuf_compiler/proto.gni")
62
63  pw_proto_library("the_service_proto") {
64    sources = [ "foo_bar/the_service.proto" ]
65  }
66
67.. admonition:: proto2 or proto3 syntax?
68
69  Always use proto3 syntax rather than proto2 for new protocol buffers. Proto2
70  protobufs can be compiled for ``pw_rpc``, but they are not as well supported
71  as proto3. Specifically, ``pw_rpc`` lacks support for non-zero default values
72  in proto2. When using Nanopb with ``pw_rpc``, proto2 response protobufs with
73  non-zero field defaults should be manually initialized to the default struct.
74
75  In the past, proto3 was sometimes avoided because it lacked support for field
76  presence detection. Fortunately, this has been fixed: proto3 now supports
77  ``optional`` fields, which are equivalent to proto2 ``optional`` fields.
78
79  If you need to distinguish between a default-valued field and a missing field,
80  mark the field as ``optional``. The presence of the field can be detected
81  with a ``HasField(name)`` or ``has_<field>`` member, depending on the library.
82
83  Optional fields have some overhead --- default-valued fields are included in
84  the encoded proto, and, if using Nanopb, the proto structs have a
85  ``has_<field>`` flag for each optional field. Use plain fields if field
86  presence detection is not needed.
87
88  .. code-block:: protobuf
89
90    syntax = "proto3";
91
92    message MyMessage {
93      // Leaving this field unset is equivalent to setting it to 0.
94      int32 number = 1;
95
96      // Setting this field to 0 is different from leaving it unset.
97      optional int32 other_number = 2;
98    }
99
1002. RPC code generation
101----------------------
102``pw_rpc`` generates a C++ header file for each ``.proto`` file. This header is
103generated in the build output directory. Its exact location varies by build
104system and toolchain, but the C++ include path always matches the sources
105declaration in the ``pw_proto_library``. The ``.proto`` extension is replaced
106with an extension corresponding to the protobuf library in use.
107
108================== =============== =============== =============
109Protobuf libraries Build subtarget Protobuf header pw_rpc header
110================== =============== =============== =============
111Raw only           .raw_rpc        (none)          .raw_rpc.pb.h
112Nanopb or raw      .nanopb_rpc     .pb.h           .rpc.pb.h
113pw_protobuf or raw .pwpb_rpc       .pwpb.h         .rpc.pwpb.h
114================== =============== =============== =============
115
116For example, the generated RPC header for ``"foo_bar/the_service.proto"`` is
117``"foo_bar/the_service.rpc.pb.h"`` for Nanopb or
118``"foo_bar/the_service.raw_rpc.pb.h"`` for raw RPCs.
119
120The generated header defines a base class for each RPC service declared in the
121``.proto`` file. A service named ``TheService`` in package ``foo.bar`` would
122generate the following base class:
123
124.. cpp:class:: template <typename Implementation> foo::bar::generated::TheService
125
1263. RPC service definition
127-------------------------
128The serivce class is implemented by inheriting from the generated RPC service
129base class and defining a method for each RPC. The methods must match the name
130and function signature for one of the supported protobuf implementations.
131Services may mix and match protobuf implementations within one service.
132
133.. tip::
134
135  The generated code includes RPC service implementation stubs. You can
136  reference or copy and paste these to get started with implementing a service.
137  These stub classes are generated at the bottom of the pw_rpc proto header.
138
139  To use the stubs, do the following:
140
141  #. Locate the generated RPC header in the build directory. For example:
142
143     .. code-block:: sh
144
145       find out/ -name <proto_name>.rpc.pb.h
146
147  #. Scroll to the bottom of the generated RPC header.
148  #. Copy the stub class declaration to a header file.
149  #. Copy the member function definitions to a source file.
150  #. Rename the class or change the namespace, if desired.
151  #. List these files in a build target with a dependency on the
152     ``pw_proto_library``.
153
154A Nanopb implementation of this service would be as follows:
155
156.. code-block:: cpp
157
158  #include "foo_bar/the_service.rpc.pb.h"
159
160  namespace foo::bar {
161
162  class TheService : public generated::TheService<TheService> {
163   public:
164    pw::Status MethodOne(ServerContext& ctx,
165                         const foo_bar_Request& request,
166                         foo_bar_Response& response) {
167      // implementation
168      return pw::OkStatus();
169    }
170
171    void MethodTwo(ServerContext& ctx,
172                   const foo_bar_Request& request,
173                   ServerWriter<foo_bar_Response>& response) {
174      // implementation
175      response.Write(foo_bar_Response{.number = 123});
176    }
177  };
178
179  }  // namespace foo::bar
180
181The Nanopb implementation would be declared in a ``BUILD.gn``:
182
183.. code-block:: python
184
185  import("//build_overrides/pigweed.gni")
186
187  import("$dir_pw_build/target_types.gni")
188
189  pw_source_set("the_service") {
190    public_configs = [ ":public" ]
191    public = [ "public/foo_bar/service.h" ]
192    public_deps = [ ":the_service_proto.nanopb_rpc" ]
193  }
194
195.. attention::
196
197  pw_rpc's generated classes will support using ``pw_protobuf`` or raw buffers
198  (no protobuf library) in the future.
199
2004. Register the service with a server
201-------------------------------------
202This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc>`
203channel output and the example service.
204
205.. code-block:: cpp
206
207  // Set up the output channel for the pw_rpc server to use. This configures the
208  // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
209  // adapt this as necessary.
210  pw::stream::SysIoWriter writer;
211  pw::rpc::RpcChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
212      writer, pw::hdlc::kDefaultRpcAddress, "HDLC output");
213
214  pw::rpc::Channel channels[] = {
215      pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
216
217  // Declare the pw_rpc server with the HDLC channel.
218  pw::rpc::Server server(channels);
219
220  pw::rpc::TheService the_service;
221
222  void RegisterServices() {
223    // Register the foo.bar.TheService example service.
224    server.Register(the_service);
225
226    // Register other services
227  }
228
229  int main() {
230    // Set up the server.
231    RegisterServices();
232
233    // Declare a buffer for decoding incoming HDLC frames.
234    std::array<std::byte, kMaxTransmissionUnit> input_buffer;
235
236    PW_LOG_INFO("Starting pw_rpc server");
237    pw::hdlc::ReadAndProcessPackets(
238        server, hdlc_channel_output, input_buffer);
239  }
240
241Channels
242========
243``pw_rpc`` sends all of its packets over channels. These are logical,
244application-layer routes used to tell the RPC system where a packet should go.
245
246Channels over a client-server connection must all have a unique ID, which can be
247assigned statically at compile time or dynamically.
248
249.. code-block:: cpp
250
251  // Creating a channel with the static ID 3.
252  pw::rpc::Channel static_channel = pw::rpc::Channel::Create<3>(&output);
253
254  // Grouping channel IDs within an enum can lead to clearer code.
255  enum ChannelId {
256    kUartChannel = 1,
257    kSpiChannel = 2,
258  };
259
260  // Creating a channel with a static ID defined within an enum.
261  pw::rpc::Channel another_static_channel =
262      pw::rpc::Channel::Create<ChannelId::kUartChannel>(&output);
263
264  // Creating a channel with a dynamic ID (note that no output is provided; it
265  // will be set when the channel is used.
266  pw::rpc::Channel dynamic_channel;
267
268Services
269========
270A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc``
271uses these .proto definitions to generate code for a base service, from which
272user-defined RPCs are implemented.
273
274``pw_rpc`` supports multiple protobuf libraries, and the generated code API
275depends on which is used.
276
277.. _module-pw_rpc-protobuf-library-apis:
278
279Protobuf library APIs
280=====================
281
282.. toctree::
283  :maxdepth: 1
284
285  nanopb/docs
286
287Testing a pw_rpc integration
288============================
289After setting up a ``pw_rpc`` server in your project, you can test that it is
290working as intended by registering the provided ``EchoService``, defined in
291``echo.proto``, which echoes back a message that it receives.
292
293.. literalinclude:: echo.proto
294  :language: protobuf
295  :lines: 14-
296
297For example, in C++ with nanopb:
298
299.. code:: c++
300
301  #include "pw_rpc/server.h"
302
303  // Include the apporpriate header for your protobuf library.
304  #include "pw_rpc/echo_service_nanopb.h"
305
306  constexpr pw::rpc::Channel kChannels[] = { /* ... */ };
307  static pw::rpc::Server server(kChannels);
308
309  static pw::rpc::EchoService echo_service;
310
311  void Init() {
312    server.RegisterService(&echo_service);
313  }
314
315Protocol description
316====================
317Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These
318packets are used to send requests and responses, control streams, cancel ongoing
319RPCs, and report errors.
320
321Packet format
322-------------
323Pigweed RPC packets consist of a type and a set of fields. The packets are
324encoded as protocol buffers. The full packet format is described in
325``pw_rpc/pw_rpc/internal/packet.proto``.
326
327.. literalinclude:: internal/packet.proto
328  :language: protobuf
329  :lines: 14-
330
331The packet type and RPC type determine which fields are present in a Pigweed RPC
332packet. Each packet type is only sent by either the client or the server.
333These tables describe the meaning of and fields included with each packet type.
334
335Client-to-server packets
336^^^^^^^^^^^^^^^^^^^^^^^^
337+---------------------------+----------------------------------+
338| packet type               | description                      |
339+===========================+==================================+
340| REQUEST                   | RPC request                      |
341|                           |                                  |
342|                           | .. code-block:: text             |
343|                           |                                  |
344|                           |   - channel_id                   |
345|                           |   - service_id                   |
346|                           |   - method_id                    |
347|                           |   - payload                      |
348|                           |     (unless first client stream) |
349|                           |                                  |
350+---------------------------+----------------------------------+
351| CLIENT_STREAM_END         | Client stream finished           |
352|                           |                                  |
353|                           | .. code-block:: text             |
354|                           |                                  |
355|                           |   - channel_id                   |
356|                           |   - service_id                   |
357|                           |   - method_id                    |
358|                           |                                  |
359|                           |                                  |
360+---------------------------+----------------------------------+
361| CLIENT_ERROR              | Received unexpected packet       |
362|                           |                                  |
363|                           | .. code-block:: text             |
364|                           |                                  |
365|                           |   - channel_id                   |
366|                           |   - service_id                   |
367|                           |   - method_id                    |
368|                           |   - status                       |
369+---------------------------+----------------------------------+
370| CANCEL_SERVER_STREAM      | Cancel a server stream           |
371|                           |                                  |
372|                           | .. code-block:: text             |
373|                           |                                  |
374|                           |   - channel_id                   |
375|                           |   - service_id                   |
376|                           |   - method_id                    |
377|                           |                                  |
378+---------------------------+----------------------------------+
379
380**Errors**
381
382The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet
383it did not request. If the RPC is a streaming RPC, the server should abort it.
384
385The status code indicates the type of error. If the client does not distinguish
386between the error types, it can send whichever status is most relevant. The
387status code is logged, but all status codes result in the same action by the
388server: aborting the RPC.
389
390* ``NOT_FOUND`` -- Received a packet for a service method the client does not
391  recognize.
392* ``FAILED_PRECONDITION`` -- Received a packet for a service method that the
393  client did not invoke.
394
395Server-to-client packets
396^^^^^^^^^^^^^^^^^^^^^^^^
397+-------------------+--------------------------------+
398| packet type       | description                    |
399+===================+================================+
400| RESPONSE          | RPC response                   |
401|                   |                                |
402|                   | .. code-block:: text           |
403|                   |                                |
404|                   |   - channel_id                 |
405|                   |   - service_id                 |
406|                   |   - method_id                  |
407|                   |   - payload                    |
408|                   |   - status                     |
409|                   |     (unless in server stream)  |
410+-------------------+--------------------------------+
411| SERVER_STREAM_END | Server stream and RPC finished |
412|                   |                                |
413|                   | .. code-block:: text           |
414|                   |                                |
415|                   |   - channel_id                 |
416|                   |   - service_id                 |
417|                   |   - method_id                  |
418|                   |   - status                     |
419+-------------------+--------------------------------+
420| SERVER_ERROR      | Received unexpected packet     |
421|                   |                                |
422|                   | .. code-block:: text           |
423|                   |                                |
424|                   |   - channel_id                 |
425|                   |   - service_id (if relevant)   |
426|                   |   - method_id (if relevant)    |
427|                   |   - status                     |
428+-------------------+--------------------------------+
429
430**Errors**
431
432The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot
433process. The client should abort any RPC for which it receives an error. The
434status field indicates the type of error.
435
436* ``NOT_FOUND`` -- The requested service or method does not exist.
437* ``FAILED_PRECONDITION`` -- Attempted to cancel an RPC that is not pending.
438* ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel
439  could not be allocated for it.
440* ``INTERNAL`` -- The server was unable to respond to an RPC due to an
441  unrecoverable internal error.
442
443Inovking a service method
444-------------------------
445Calling an RPC requires a specific sequence of packets. This section describes
446the protocol for calling service methods of each type: unary, server streaming,
447client streaming, and bidirectional streaming.
448
449Unary RPC
450^^^^^^^^^
451In a unary RPC, the client sends a single request and the server sends a single
452response.
453
454.. seqdiag::
455  :scale: 110
456
457  seqdiag {
458    default_note_color = aliceblue;
459
460    client -> server [
461        label = "request",
462        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
463    ];
464
465    client <- server [
466        label = "response",
467        rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
468    ];
469  }
470
471Server streaming RPC
472^^^^^^^^^^^^^^^^^^^^
473In a server streaming RPC, the client sends a single request and the server
474sends any number of responses followed by a ``SERVER_STREAM_END`` packet.
475
476.. seqdiag::
477  :scale: 110
478
479  seqdiag {
480    default_note_color = aliceblue;
481
482    client -> server [
483        label = "request",
484        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
485    ];
486
487    client <-- server [
488        noactivate,
489        label = "responses (zero or more)",
490        rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
491    ];
492
493    client <- server [
494        label = "done",
495        rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
496    ];
497  }
498
499Server streaming RPCs may be cancelled by the client. The client sends a
500``CANCEL_SERVER_STREAM`` packet to terminate the RPC.
501
502.. seqdiag::
503  :scale: 110
504
505  seqdiag {
506    default_note_color = aliceblue;
507
508    client -> server [
509        label = "request",
510        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
511    ];
512
513    client <-- server [
514        noactivate,
515        label = "responses (zero or more)",
516        rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
517    ];
518
519    client -> server [
520        noactivate,
521        label = "cancel",
522        leftnote  = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
523    ];
524
525    client <- server [
526        label = "done",
527        rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
528    ];
529  }
530
531Client streaming RPC
532^^^^^^^^^^^^^^^^^^^^
533In a client streaming RPC, the client sends any number of RPC requests followed
534by a ``CLIENT_STREAM_END`` packet. The server then sends a single response.
535
536The first client-to-server RPC packet does not include a payload.
537
538.. attention::
539
540  ``pw_rpc`` does not yet support client streaming RPCs.
541
542.. seqdiag::
543  :scale: 110
544
545  seqdiag {
546    default_note_color = aliceblue;
547
548    client -> server [
549        label = "start",
550        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
551    ];
552
553    client --> server [
554        noactivate,
555        label = "requests (zero or more)",
556        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
557    ];
558
559    client -> server [
560        noactivate,
561        label = "done",
562        leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
563    ];
564
565    client <- server [
566        label = "response",
567        rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
568    ];
569  }
570
571The server may terminate a client streaming RPC at any time by sending its
572response packet.
573
574.. seqdiag::
575  :scale: 110
576
577  seqdiag {
578    default_note_color = aliceblue;
579
580    client -> server [
581        label = "start",
582        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
583    ];
584
585    client --> server [
586        noactivate,
587        label = "requests (zero or more)",
588        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
589    ];
590
591    client <- server [
592        label = "response",
593        rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
594    ];
595  }
596
597Bidirectional streaming RPC
598^^^^^^^^^^^^^^^^^^^^^^^^^^^
599In a bidirectional streaming RPC, the client sends any number of requests and
600the server sends any number of responses. The client sends a
601``CLIENT_STREAM_END`` packet when it has finished sending requests. The server
602sends a ``SERVER_STREAM_END`` packet after it receives the client's
603``CLIENT_STREAM_END`` and finished sending its responses.
604
605The first client-to-server RPC packet does not include a payload.
606
607.. attention::
608
609  ``pw_rpc`` does not yet support bidirectional streaming RPCs.
610
611.. seqdiag::
612  :scale: 110
613
614  seqdiag {
615    default_note_color = aliceblue;
616
617    client -> server [
618        label = "start",
619        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
620    ];
621
622    client --> server [
623        noactivate,
624        label = "requests (zero or more)",
625        leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
626    ];
627
628    ... (messages in any order) ...
629
630    client <-- server [
631        noactivate,
632        label = "responses (zero or more)",
633        rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
634    ];
635
636    client -> server [
637        noactivate,
638        label = "done",
639        leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
640    ];
641
642    client <-- server [
643        noactivate,
644        label = "responses (zero or more)",
645        rightnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
646    ];
647
648    client <- server [
649        label = "done",
650        rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
651    ];
652  }
653
654The server may terminate the RPC at any time by sending a ``SERVER_STREAM_END``
655packet with the status, even if the client has not sent its ``STREAM_END``. The
656client may cancel the RPC at any time by sending a ``CANCEL_SERVER_STREAM``
657packet.
658
659.. seqdiag::
660  :scale: 110
661
662  seqdiag {
663    default_note_color = aliceblue;
664
665    client -> server [
666        label = "start",
667        leftnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID"
668    ];
669
670    client --> server [
671        noactivate,
672        label = "requests (zero or more)",
673        leftnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
674    ];
675
676    client <-- server [
677        noactivate,
678        label = "responses (zero or more)",
679        rightnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
680    ];
681
682    client -> server [
683        noactivate,
684        label = "cancel",
685        leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
686    ];
687
688    client <- server [
689        label = "done",
690        rightnote = "PacketType.STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
691    ];
692  }
693
694RPC server
695==========
696Declare an instance of ``rpc::Server`` and register services with it.
697
698.. admonition:: TODO
699
700  Document the public interface
701
702Size report
703-----------
704The following size report showcases the memory usage of the core RPC server. It
705is configured with a single channel using a basic transport interface that
706directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
707packet buffer, which comprises the plurality of the example's RAM usage. This is
708not a suitable transport for an actual product; a real implementation would have
709additional overhead proportional to the complexity of the transport.
710
711.. include:: server_size
712
713RPC server implementation
714-------------------------
715
716The Method class
717^^^^^^^^^^^^^^^^
718The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
719serves as the bridge between the ``pw_rpc`` server library and the user-defined
720RPC functions. Each supported protobuf implementation extends ``Method`` to
721implement its request and response proto handling. The ``pw_rpc`` server
722calls into the ``Method`` implementation through the base class's ``Invoke``
723function.
724
725``Method`` implementations store metadata about each method, including a
726function pointer to the user-defined method implementation. They also provide
727``static constexpr`` functions for creating each type of method. ``Method``
728implementations must satisfy the ``MethodImplTester`` test class in
729``pw_rpc_private/method_impl_tester.h``.
730
731See ``pw_rpc/internal/method.h`` for more details about ``Method``.
732
733Packet flow
734^^^^^^^^^^^
735
736Requests
737~~~~~~~~
738
739.. blockdiag::
740
741  blockdiag {
742    packets [shape = beginpoint];
743
744    group {
745      label = "pw_rpc library";
746
747      server [label = "Server"];
748      service [label = "Service"];
749      method [label = "internal::Method"];
750    }
751
752    stubs [label = "generated services", shape = ellipse];
753    user [label = "user-defined RPCs", shape = roundedbox];
754
755    packets -> server -> service -> method -> stubs -> user;
756    packets -> server [folded];
757    method -> stubs [folded];
758  }
759
760Responses
761~~~~~~~~~
762
763.. blockdiag::
764
765  blockdiag {
766    user -> stubs [folded];
767
768    group {
769      label = "pw_rpc library";
770
771      server [label = "Server"];
772      method [label = "internal::Method"];
773      channel [label = "Channel"];
774    }
775
776    stubs [label = "generated services", shape = ellipse];
777    user [label = "user-defined RPCs", shape = roundedbox];
778    packets [shape = beginpoint];
779
780    user -> stubs -> method [folded];
781    method -> server -> channel;
782    channel -> packets [folded];
783  }
784
785RPC client
786==========
787The RPC client is used to send requests to a server and manages the contexts of
788ongoing RPCs.
789
790Setting up a client
791-------------------
792The ``pw::rpc::Client`` class is instantiated with a list of channels that it
793uses to communicate. These channels can be shared with a server, but multiple
794clients cannot use the same channels.
795
796To send incoming RPC packets from the transport layer to be processed by a
797client, the client's ``ProcessPacket`` function is called with the packet data.
798
799.. code:: c++
800
801  #include "pw_rpc/client.h"
802
803  namespace {
804
805  pw::rpc::Channel my_channels[] = {
806      pw::rpc::Channel::Create<1>(&my_channel_output)};
807  pw::rpc::Client my_client(my_channels);
808
809  }  // namespace
810
811  // Called when the transport layer receives an RPC packet.
812  void ProcessRpcPacket(ConstByteSpan packet) {
813    my_client.ProcessPacket(packet);
814  }
815
816.. _module-pw_rpc-making-calls:
817
818Making RPC calls
819----------------
820RPC calls are not made directly through the client, but using one of its
821registered channels instead. A service client class is generated from a .proto
822file for each selected protobuf library, which is then used to send RPC requests
823through a given channel. The API for this depends on the protobuf library;
824please refer to the
825:ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple
826service client implementations can exist simulatenously and share the same
827``Client`` class.
828
829When a call is made, a ``pw::rpc::ClientCall`` object is returned to the caller.
830This object tracks the ongoing RPC call, and can be used to manage it. An RPC
831call is only active as long as its ``ClientCall`` object is alive.
832
833.. tip::
834  Use ``std::move`` when passing around ``ClientCall`` objects to keep RPCs
835  alive.
836
837Client implementation details
838-----------------------------
839
840The ClientCall class
841^^^^^^^^^^^^^^^^^^^^
842``ClientCall`` stores the context of an active RPC, and serves as the user's
843interface to the RPC client. The core RPC library provides a base ``ClientCall``
844class with common functionality, which is then extended for RPC client
845implementations tied to different protobuf libraries to provide convenient
846interfaces for working with RPCs.
847
848The RPC server stores a list of all of active ``ClientCall`` objects. When an
849incoming packet is recieved, it dispatches to one of its active calls, which
850then decodes the payload and presents it to the user.
851
852ClientServer
853============
854Sometimes, a device needs to both process RPCs as a server, as well as making
855calls to another device as a client. To do this, both a client and server must
856be set up, and incoming packets must be sent to both of them.
857
858Pigweed simplifies this setup by providing a ``ClientServer`` class which wraps
859an RPC client and server with the same set of channels.
860
861.. code-block:: cpp
862
863  pw::rpc::Channel channels[] = {
864      pw::rpc::Channel::Create<1>(&channel_output)};
865
866  // Creates both a client and a server.
867  pw::rpc::ClientServer client_server(channels);
868
869  void ProcessRpcData(pw::ConstByteSpan packet) {
870    // Calls into both the client and the server, sending the packet to the
871    // appropriate one.
872    client_server.ProcessPacket(packet, output);
873  }
874