blob: daece792f945567158a4deb26e623add84b89a3c [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
17
Wyatt Hepler455b4922020-09-18 00:19:21 -070018.. admonition:: Try it out!
19
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -070020 For a quick intro to ``pw_rpc``, see the
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080021 :ref:`module-pw_hdlc-rpc-example` in the :ref:`module-pw_hdlc` module.
Wyatt Hepler455b4922020-09-18 00:19:21 -070022
Wyatt Hepler067dd7e2020-07-14 19:34:32 -070023.. attention::
Alexei Frolov26e3ae62020-05-04 17:06:17 -070024
Wyatt Hepler455b4922020-09-18 00:19:21 -070025 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
Wyatt Hepler830d26d2021-02-17 09:07:43 -080067.. 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
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08001002. 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.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700107
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800108================== =============== =============== =============
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:
Wyatt Hepler455b4922020-09-18 00:19:21 -0700123
124.. cpp:class:: template <typename Implementation> foo::bar::generated::TheService
125
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08001263. 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
Wyatt Heplerdf38ed12021-03-24 08:42:48 -0700139 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
Wyatt Hepler455b4922020-09-18 00:19:21 -0700154A Nanopb implementation of this service would be as follows:
155
156.. code-block:: cpp
157
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800158 #include "foo_bar/the_service.rpc.pb.h"
159
Wyatt Hepler455b4922020-09-18 00:19:21 -0700160 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
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800168 return pw::OkStatus();
Wyatt Hepler455b4922020-09-18 00:19:21 -0700169 }
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" ]
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800192 public_deps = [ ":the_service_proto.nanopb_rpc" ]
Wyatt Hepler455b4922020-09-18 00:19:21 -0700193 }
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
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08002004. Register the service with a server
Wyatt Hepler455b4922020-09-18 00:19:21 -0700201-------------------------------------
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800202This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc>`
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700203channel output and the example service.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700204
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(
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800212 writer, pw::hdlc::kDefaultRpcAddress, "HDLC output");
Wyatt Hepler455b4922020-09-18 00:19:21 -0700213
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");
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800237 pw::hdlc::ReadAndProcessPackets(
Wyatt Hepler455b4922020-09-18 00:19:21 -0700238 server, hdlc_channel_output, input_buffer);
239 }
Wyatt Hepler948f5472020-06-02 16:52:28 -0700240
Alexei Frolov1c670a22021-04-09 10:18:17 -0700241Channels
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
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700268Services
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
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700277.. _module-pw_rpc-protobuf-library-apis:
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700278
279Protobuf library APIs
280=====================
281
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700282.. 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
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800291``echo.proto``, which echoes back a message that it receives.
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700292
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800293.. literalinclude:: echo.proto
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700294 :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
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700315Protocol 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
Wyatt Heplerba325e42021-03-08 14:23:34 -0800325``pw_rpc/pw_rpc/internal/packet.proto``.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700326
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800327.. literalinclude:: internal/packet.proto
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700328 :language: protobuf
329 :lines: 14-
330
331The packet type and RPC type determine which fields are present in a Pigweed RPC
Wyatt Hepler0f262352020-07-29 09:51:27 -0700332packet. 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.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700334
Wyatt Hepler0f262352020-07-29 09:51:27 -0700335Client-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+---------------------------+----------------------------------+
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700379
Wyatt Hepler0f262352020-07-29 09:51:27 -0700380**Errors**
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700381
Wyatt Hepler0f262352020-07-29 09:51:27 -0700382The 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.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700437* ``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.
Wyatt Hepler712d3672020-07-13 15:52:11 -0700440* ``INTERNAL`` -- The server was unable to respond to an RPC due to an
441 unrecoverable internal error.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700442
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",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700462 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700463 ];
464
465 client <- server [
466 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700467 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700468 ];
469 }
470
471Server streaming RPC
472^^^^^^^^^^^^^^^^^^^^
473In a server streaming RPC, the client sends a single request and the server
Wyatt Hepler0f262352020-07-29 09:51:27 -0700474sends any number of responses followed by a ``SERVER_STREAM_END`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700475
476.. seqdiag::
477 :scale: 110
478
479 seqdiag {
480 default_note_color = aliceblue;
481
482 client -> server [
483 label = "request",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700484 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700485 ];
486
487 client <-- server [
488 noactivate,
489 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700490 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700491 ];
492
493 client <- server [
494 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700495 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700496 ];
497 }
498
499Server streaming RPCs may be cancelled by the client. The client sends a
Wyatt Hepler0f262352020-07-29 09:51:27 -0700500``CANCEL_SERVER_STREAM`` packet to terminate the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700501
502.. seqdiag::
503 :scale: 110
504
505 seqdiag {
506 default_note_color = aliceblue;
507
508 client -> server [
509 label = "request",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700510 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700511 ];
512
513 client <-- server [
514 noactivate,
515 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700516 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700517 ];
518
519 client -> server [
520 noactivate,
521 label = "cancel",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700522 leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700523 ];
524
525 client <- server [
526 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700527 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700528 ];
529 }
530
531Client streaming RPC
532^^^^^^^^^^^^^^^^^^^^
533In a client streaming RPC, the client sends any number of RPC requests followed
Wyatt Hepler0f262352020-07-29 09:51:27 -0700534by a ``CLIENT_STREAM_END`` packet. The server then sends a single response.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700535
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",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700550 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700551 ];
552
553 client --> server [
554 noactivate,
555 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700556 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700557 ];
558
559 client -> server [
560 noactivate,
561 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700562 leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700563 ];
564
565 client <- server [
566 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700567 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700568 ];
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",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700582 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700583 ];
584
585 client --> server [
586 noactivate,
587 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700588 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700589 ];
590
591 client <- server [
592 label = "response",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700593 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700594 ];
595 }
596
597Bidirectional streaming RPC
598^^^^^^^^^^^^^^^^^^^^^^^^^^^
599In a bidirectional streaming RPC, the client sends any number of requests and
Wyatt Hepler0f262352020-07-29 09:51:27 -0700600the 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.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700604
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",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700619 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700620 ];
621
622 client --> server [
623 noactivate,
624 label = "requests (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700625 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700626 ];
627
628 ... (messages in any order) ...
629
630 client <-- server [
631 noactivate,
632 label = "responses (zero or more)",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700633 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700634 ];
635
636 client -> server [
637 noactivate,
638 label = "done",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700639 leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700640 ];
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",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700650 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700651 ];
652 }
653
Wyatt Hepler0f262352020-07-29 09:51:27 -0700654The 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.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700658
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",
Wyatt Hepler0f262352020-07-29 09:51:27 -0700685 leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700686 ];
687
688 client <- server [
689 label = "done",
690 rightnote = "PacketType.STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
691 ];
692 }
693
Wyatt Hepler948f5472020-06-02 16:52:28 -0700694RPC server
695==========
696Declare an instance of ``rpc::Server`` and register services with it.
697
698.. admonition:: TODO
699
700 Document the public interface
701
Alexei Frolovbf33d212020-09-15 17:13:45 -0700702Size 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
Wyatt Hepler948f5472020-06-02 16:52:28 -0700713RPC 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
Wyatt Heplere95bd722020-11-23 07:49:47 -0800720RPC 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.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700724
Wyatt Heplere95bd722020-11-23 07:49:47 -0800725``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``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700730
Wyatt Heplere95bd722020-11-23 07:49:47 -0800731See ``pw_rpc/internal/method.h`` for more details about ``Method``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700732
733Packet flow
734^^^^^^^^^^^
735
736Requests
737~~~~~~~~
738
739.. blockdiag::
740
741 blockdiag {
742 packets [shape = beginpoint];
743
744 group {
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700745 label = "pw_rpc library";
Wyatt Hepler948f5472020-06-02 16:52:28 -0700746
747 server [label = "Server"];
Alexei Frolov9a4d6bf2020-08-04 10:33:26 -0700748 service [label = "Service"];
Wyatt Hepler948f5472020-06-02 16:52:28 -0700749 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 {
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700769 label = "pw_rpc library";
Wyatt Hepler948f5472020-06-02 16:52:28 -0700770
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 }
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700784
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
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700816.. _module-pw_rpc-making-calls:
817
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700818Making 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;
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700824please 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.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700828
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.
Alexei Frolov3e280922021-04-12 14:53:06 -0700851
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 }