blob: c28cdc94223fa8480dce8be53b7a9fb94b3ca626 [file] [log] [blame]
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -07001.. _module-pw_rpc:
Alexei Frolov26e3ae62020-05-04 17:06:17 -07002
3------
4pw_rpc
5------
6The ``pw_rpc`` module provides a system for defining and invoking remote
7procedure calls (RPCs) on a device.
8
Wyatt Hepler830d26d2021-02-17 09:07:43 -08009This document discusses the ``pw_rpc`` protocol and its C++ implementation.
10``pw_rpc`` implementations for other languages are described in their own
11documents:
12
13.. toctree::
14 :maxdepth: 1
15
16 py/docs
Jared Weinsteind4649112021-08-19 12:27:23 -070017 ts/docs
Wyatt Hepler830d26d2021-02-17 09:07:43 -080018
Wyatt Hepler455b4922020-09-18 00:19:21 -070019.. admonition:: Try it out!
20
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -070021 For a quick intro to ``pw_rpc``, see the
Alexei Frolovd3e5cb72021-01-08 13:08:45 -080022 :ref:`module-pw_hdlc-rpc-example` in the :ref:`module-pw_hdlc` module.
Wyatt Hepler455b4922020-09-18 00:19:21 -070023
Wyatt Hepler067dd7e2020-07-14 19:34:32 -070024.. attention::
Alexei Frolov26e3ae62020-05-04 17:06:17 -070025
Wyatt Hepler455b4922020-09-18 00:19:21 -070026 This documentation is under construction.
27
Wyatt Hepler85e91402021-08-05 12:48:03 -070028RPC semantics
29=============
30The semantics of ``pw_rpc`` are similar to `gRPC
31<https://grpc.io/docs/what-is-grpc/core-concepts/>`_.
32
33RPC call lifecycle
34------------------
35In ``pw_rpc``, an RPC begins when the client sends a request packet. The server
36receives the request, looks up the relevant service method, then calls into the
37RPC function. The RPC is considered active until the server sends a response
38packet with the RPC's status. The client may terminate an ongoing RPC by
39cancelling it.
40
41``pw_rpc`` supports only one RPC invocation per service/method/channel. If a
42client calls an ongoing RPC on the same channel, the server cancels the ongoing
43call and reinvokes the RPC with the new request. This applies to unary and
44streaming RPCs, though the server may not have an opportunity to cancel a
45synchronously handled unary RPC before it completes. The same RPC may be invoked
46multiple times simultaneously if the invocations are on different channels.
47
Wyatt Hepler5a3a36b2021-09-08 11:15:05 -070048Unrequested responses
49---------------------
50``pw_rpc`` supports sending responses to RPCs that have not yet been invoked by
51a client. This is useful in testing and in situations like an RPC that triggers
52reboot. After the reboot, the device opens the writer object and sends its
53response to the client.
54
Wyatt Heplerd95e1e92021-09-14 22:10:51 -070055The C++ API for opening a server reader/writer takes the generated RPC function
56as a template parameter. The server to use, channel ID, and service instance are
57passed as arguments. The API is the same for all RPC types, except the
58appropriate reader/writer class must be used.
Wyatt Hepler5a3a36b2021-09-08 11:15:05 -070059
Wyatt Heplerd95e1e92021-09-14 22:10:51 -070060.. code-block:: c++
61
62 // Open a ServerWriter for a server streaming RPC.
63 auto writer = RawServerWriter::Open<pw_rpc::raw::ServiceName::MethodName>(
64 server, channel_id, service_instance);
65
66 // Send some responses, even though the client has not yet called this RPC.
67 CHECK_OK(writer.Write(encoded_response_1));
68 CHECK_OK(writer.Write(encoded_response_2));
69
70 // Finish the RPC.
71 CHECK_OK(writer.Finish(OkStatus()));
Wyatt Hepler5a3a36b2021-09-08 11:15:05 -070072
Wyatt Hepler455b4922020-09-18 00:19:21 -070073Creating an RPC
74===============
75
761. RPC service declaration
77--------------------------
78Pigweed RPCs are declared in a protocol buffer service definition.
79
80* `Protocol Buffer service documentation
81 <https://developers.google.com/protocol-buffers/docs/proto3#services>`_
82* `gRPC service definition documentation
83 <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_
84
85.. code-block:: protobuf
86
87 syntax = "proto3";
88
89 package foo.bar;
90
91 message Request {}
92
93 message Response {
94 int32 number = 1;
95 }
96
97 service TheService {
98 rpc MethodOne(Request) returns (Response) {}
99 rpc MethodTwo(Request) returns (stream Response) {}
100 }
101
102This protocol buffer is declared in a ``BUILD.gn`` file as follows:
103
104.. code-block:: python
105
106 import("//build_overrides/pigweed.gni")
107 import("$dir_pw_protobuf_compiler/proto.gni")
108
109 pw_proto_library("the_service_proto") {
110 sources = [ "foo_bar/the_service.proto" ]
111 }
112
Wyatt Hepler830d26d2021-02-17 09:07:43 -0800113.. admonition:: proto2 or proto3 syntax?
114
115 Always use proto3 syntax rather than proto2 for new protocol buffers. Proto2
116 protobufs can be compiled for ``pw_rpc``, but they are not as well supported
117 as proto3. Specifically, ``pw_rpc`` lacks support for non-zero default values
118 in proto2. When using Nanopb with ``pw_rpc``, proto2 response protobufs with
119 non-zero field defaults should be manually initialized to the default struct.
120
121 In the past, proto3 was sometimes avoided because it lacked support for field
122 presence detection. Fortunately, this has been fixed: proto3 now supports
123 ``optional`` fields, which are equivalent to proto2 ``optional`` fields.
124
125 If you need to distinguish between a default-valued field and a missing field,
126 mark the field as ``optional``. The presence of the field can be detected
127 with a ``HasField(name)`` or ``has_<field>`` member, depending on the library.
128
129 Optional fields have some overhead --- default-valued fields are included in
130 the encoded proto, and, if using Nanopb, the proto structs have a
131 ``has_<field>`` flag for each optional field. Use plain fields if field
132 presence detection is not needed.
133
134 .. code-block:: protobuf
135
136 syntax = "proto3";
137
138 message MyMessage {
139 // Leaving this field unset is equivalent to setting it to 0.
140 int32 number = 1;
141
142 // Setting this field to 0 is different from leaving it unset.
143 optional int32 other_number = 2;
144 }
145
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08001462. RPC code generation
147----------------------
148``pw_rpc`` generates a C++ header file for each ``.proto`` file. This header is
149generated in the build output directory. Its exact location varies by build
150system and toolchain, but the C++ include path always matches the sources
151declaration in the ``pw_proto_library``. The ``.proto`` extension is replaced
152with an extension corresponding to the protobuf library in use.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700153
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800154================== =============== =============== =============
155Protobuf libraries Build subtarget Protobuf header pw_rpc header
156================== =============== =============== =============
157Raw only .raw_rpc (none) .raw_rpc.pb.h
158Nanopb or raw .nanopb_rpc .pb.h .rpc.pb.h
159pw_protobuf or raw .pwpb_rpc .pwpb.h .rpc.pwpb.h
160================== =============== =============== =============
161
162For example, the generated RPC header for ``"foo_bar/the_service.proto"`` is
163``"foo_bar/the_service.rpc.pb.h"`` for Nanopb or
164``"foo_bar/the_service.raw_rpc.pb.h"`` for raw RPCs.
165
166The generated header defines a base class for each RPC service declared in the
167``.proto`` file. A service named ``TheService`` in package ``foo.bar`` would
Wyatt Heplerd2496322021-09-10 17:22:14 -0700168generate the following base class for Nanopb:
Wyatt Hepler455b4922020-09-18 00:19:21 -0700169
Wyatt Heplerd2496322021-09-10 17:22:14 -0700170.. cpp:class:: template <typename Implementation> foo::bar::pw_rpc::nanopb::TheService::Service
Wyatt Hepler455b4922020-09-18 00:19:21 -0700171
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08001723. RPC service definition
173-------------------------
174The serivce class is implemented by inheriting from the generated RPC service
175base class and defining a method for each RPC. The methods must match the name
176and function signature for one of the supported protobuf implementations.
177Services may mix and match protobuf implementations within one service.
178
179.. tip::
180
181 The generated code includes RPC service implementation stubs. You can
182 reference or copy and paste these to get started with implementing a service.
183 These stub classes are generated at the bottom of the pw_rpc proto header.
184
Wyatt Heplerdf38ed12021-03-24 08:42:48 -0700185 To use the stubs, do the following:
186
187 #. Locate the generated RPC header in the build directory. For example:
188
189 .. code-block:: sh
190
191 find out/ -name <proto_name>.rpc.pb.h
192
193 #. Scroll to the bottom of the generated RPC header.
194 #. Copy the stub class declaration to a header file.
195 #. Copy the member function definitions to a source file.
196 #. Rename the class or change the namespace, if desired.
197 #. List these files in a build target with a dependency on the
198 ``pw_proto_library``.
199
Wyatt Hepler455b4922020-09-18 00:19:21 -0700200A Nanopb implementation of this service would be as follows:
201
202.. code-block:: cpp
203
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800204 #include "foo_bar/the_service.rpc.pb.h"
205
Wyatt Hepler455b4922020-09-18 00:19:21 -0700206 namespace foo::bar {
207
Wyatt Heplerd2496322021-09-10 17:22:14 -0700208 class TheService : public pw_rpc::nanopb::TheService::Service<TheService> {
Wyatt Hepler455b4922020-09-18 00:19:21 -0700209 public:
Wyatt Heplerebf33b72021-09-09 13:33:56 -0700210 pw::Status MethodOne(ServerContext&,
Wyatt Hepler455b4922020-09-18 00:19:21 -0700211 const foo_bar_Request& request,
212 foo_bar_Response& response) {
213 // implementation
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800214 return pw::OkStatus();
Wyatt Hepler455b4922020-09-18 00:19:21 -0700215 }
216
Wyatt Heplerebf33b72021-09-09 13:33:56 -0700217 void MethodTwo(ServerContext&,
Wyatt Hepler455b4922020-09-18 00:19:21 -0700218 const foo_bar_Request& request,
219 ServerWriter<foo_bar_Response>& response) {
220 // implementation
221 response.Write(foo_bar_Response{.number = 123});
222 }
223 };
224
225 } // namespace foo::bar
226
227The Nanopb implementation would be declared in a ``BUILD.gn``:
228
229.. code-block:: python
230
231 import("//build_overrides/pigweed.gni")
232
233 import("$dir_pw_build/target_types.gni")
234
235 pw_source_set("the_service") {
236 public_configs = [ ":public" ]
237 public = [ "public/foo_bar/service.h" ]
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800238 public_deps = [ ":the_service_proto.nanopb_rpc" ]
Wyatt Hepler455b4922020-09-18 00:19:21 -0700239 }
240
241.. attention::
242
243 pw_rpc's generated classes will support using ``pw_protobuf`` or raw buffers
244 (no protobuf library) in the future.
245
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08002464. Register the service with a server
Wyatt Hepler455b4922020-09-18 00:19:21 -0700247-------------------------------------
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800248This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc>`
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700249channel output and the example service.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700250
251.. code-block:: cpp
252
253 // Set up the output channel for the pw_rpc server to use. This configures the
254 // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
255 // adapt this as necessary.
256 pw::stream::SysIoWriter writer;
257 pw::rpc::RpcChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800258 writer, pw::hdlc::kDefaultRpcAddress, "HDLC output");
Wyatt Hepler455b4922020-09-18 00:19:21 -0700259
260 pw::rpc::Channel channels[] = {
261 pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
262
263 // Declare the pw_rpc server with the HDLC channel.
264 pw::rpc::Server server(channels);
265
266 pw::rpc::TheService the_service;
267
268 void RegisterServices() {
269 // Register the foo.bar.TheService example service.
270 server.Register(the_service);
271
272 // Register other services
273 }
274
275 int main() {
276 // Set up the server.
277 RegisterServices();
278
279 // Declare a buffer for decoding incoming HDLC frames.
280 std::array<std::byte, kMaxTransmissionUnit> input_buffer;
281
282 PW_LOG_INFO("Starting pw_rpc server");
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800283 pw::hdlc::ReadAndProcessPackets(
Wyatt Hepler455b4922020-09-18 00:19:21 -0700284 server, hdlc_channel_output, input_buffer);
285 }
Wyatt Hepler948f5472020-06-02 16:52:28 -0700286
Alexei Frolov1c670a22021-04-09 10:18:17 -0700287Channels
288========
289``pw_rpc`` sends all of its packets over channels. These are logical,
290application-layer routes used to tell the RPC system where a packet should go.
291
292Channels over a client-server connection must all have a unique ID, which can be
293assigned statically at compile time or dynamically.
294
295.. code-block:: cpp
296
297 // Creating a channel with the static ID 3.
298 pw::rpc::Channel static_channel = pw::rpc::Channel::Create<3>(&output);
299
300 // Grouping channel IDs within an enum can lead to clearer code.
301 enum ChannelId {
302 kUartChannel = 1,
303 kSpiChannel = 2,
304 };
305
306 // Creating a channel with a static ID defined within an enum.
307 pw::rpc::Channel another_static_channel =
308 pw::rpc::Channel::Create<ChannelId::kUartChannel>(&output);
309
310 // Creating a channel with a dynamic ID (note that no output is provided; it
311 // will be set when the channel is used.
312 pw::rpc::Channel dynamic_channel;
313
Alexei Frolov567e6702021-04-13 09:13:02 -0700314Sometimes, the ID and output of a channel are not known at compile time as they
315depend on information stored on the physical device. To support this use case, a
316dynamically-assignable channel can be configured once at runtime with an ID and
317output.
318
319.. code-block:: cpp
320
321 // Create a dynamic channel without a compile-time ID or output.
322 pw::rpc::Channel dynamic_channel;
323
324 void Init() {
325 // Called during boot to pull the channel configuration from the system.
326 dynamic_channel.Configure(GetChannelId(), some_output);
327 }
328
329
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700330Services
331========
332A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc``
333uses these .proto definitions to generate code for a base service, from which
334user-defined RPCs are implemented.
335
336``pw_rpc`` supports multiple protobuf libraries, and the generated code API
337depends on which is used.
338
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700339.. _module-pw_rpc-protobuf-library-apis:
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700340
341Protobuf library APIs
342=====================
343
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700344.. toctree::
345 :maxdepth: 1
346
347 nanopb/docs
348
349Testing a pw_rpc integration
350============================
351After setting up a ``pw_rpc`` server in your project, you can test that it is
352working as intended by registering the provided ``EchoService``, defined in
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800353``echo.proto``, which echoes back a message that it receives.
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700354
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800355.. literalinclude:: echo.proto
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700356 :language: protobuf
357 :lines: 14-
358
359For example, in C++ with nanopb:
360
361.. code:: c++
362
363 #include "pw_rpc/server.h"
364
365 // Include the apporpriate header for your protobuf library.
366 #include "pw_rpc/echo_service_nanopb.h"
367
368 constexpr pw::rpc::Channel kChannels[] = { /* ... */ };
369 static pw::rpc::Server server(kChannels);
370
371 static pw::rpc::EchoService echo_service;
372
373 void Init() {
Wyatt Hepler31b16ea2021-07-21 08:52:02 -0700374 server.RegisterService(echo_service);
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700375 }
376
Wyatt Hepler31b16ea2021-07-21 08:52:02 -0700377Benchmarking and stress testing
378-------------------------------
379
380.. toctree::
381 :maxdepth: 1
382 :hidden:
383
384 benchmark
385
386``pw_rpc`` provides an RPC service and Python module for stress testing and
387benchmarking a ``pw_rpc`` deployment. See :ref:`module-pw_rpc-benchmark`.
388
Wyatt Hepler05d860d2021-09-22 09:43:10 -0700389Naming
390======
391
392Reserved names
393--------------
394``pw_rpc`` reserves a few service method names so they can be used for generated
395classes. The following names cannnot be used for service methods:
396
397- ``Client``
398- ``Service``
399- Any reserved words in the languages ``pw_rpc`` supports (e.g. ``class``).
400
401``pw_rpc`` does not reserve any service names, but the restriction of avoiding
402reserved words in supported languages applies.
403
404Service naming style
405--------------------
406``pw_rpc`` service names should use capitalized camel case and should not use
407the term "Service". Appending "Service" to a service name is redundant, similar
408to appending "Class" or "Function" to a class or function name. The
409C++ implementation class may use "Service" in its name, however.
410
411For example, a service for accessing a file system should simply be named
412``service FileSystem``, rather than ``service FileSystemService``, in the
413``.proto`` file.
414
415.. code-block:: protobuf
416
417 // file.proto
418 package pw.file;
419
420 service FileSystem {
421 rpc List(ListRequest) returns (stream ListResponse);
422 }
423
424The C++ service implementation class may append "Service" to the name.
425
426.. code-block:: cpp
427
428 // file_system_service.h
429 #include "pw_file/file.raw_rpc.pb.h"
430
431 namespace pw::file {
432
433 class FileSystemService : public pw_rpc::raw::FileSystem::Service<FileSystemService> {
434 void List(ServerContext&, ConstByteSpan request, RawServerWriter& writer);
435 };
436
437 }
438
439For upstream Pigweed services, this naming style is a requirement. Note that
440some services created before this was established may use non-compliant
441names. For Pigweed users, this naming style is a suggestion.
442
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700443Protocol description
444====================
445Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These
446packets are used to send requests and responses, control streams, cancel ongoing
447RPCs, and report errors.
448
449Packet format
450-------------
451Pigweed RPC packets consist of a type and a set of fields. The packets are
452encoded as protocol buffers. The full packet format is described in
Wyatt Heplerba325e42021-03-08 14:23:34 -0800453``pw_rpc/pw_rpc/internal/packet.proto``.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700454
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800455.. literalinclude:: internal/packet.proto
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700456 :language: protobuf
457 :lines: 14-
458
459The packet type and RPC type determine which fields are present in a Pigweed RPC
Wyatt Hepler0f262352020-07-29 09:51:27 -0700460packet. Each packet type is only sent by either the client or the server.
461These tables describe the meaning of and fields included with each packet type.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700462
Wyatt Hepler0f262352020-07-29 09:51:27 -0700463Client-to-server packets
464^^^^^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700465+-------------------+-------------------------------------+
466| packet type | description |
467+===================+=====================================+
468| REQUEST | Invoke an RPC |
469| | |
470| | .. code-block:: text |
471| | |
472| | - channel_id |
473| | - service_id |
474| | - method_id |
475| | - payload |
476| | (unary & server streaming only) |
477| | |
478+-------------------+-------------------------------------+
479| CLIENT_STREAM | Message in a client stream |
480| | |
481| | .. code-block:: text |
482| | |
483| | - channel_id |
484| | - service_id |
485| | - method_id |
486| | - payload |
487| | |
488+-------------------+-------------------------------------+
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700489| CLIENT_STREAM_END | Client stream is complete |
490| | |
491| | .. code-block:: text |
492| | |
493| | - channel_id |
494| | - service_id |
495| | - method_id |
496| | |
497+-------------------+-------------------------------------+
Wyatt Heplera9211162021-06-12 15:40:11 -0700498| CLIENT_ERROR | Received unexpected packet |
499| | |
500| | .. code-block:: text |
501| | |
502| | - channel_id |
503| | - service_id |
504| | - method_id |
505| | - status |
506| | |
507+-------------------+-------------------------------------+
508| CANCEL | Cancel an ongoing RPC |
509| | |
510| | .. code-block:: text |
511| | |
512| | - channel_id |
513| | - service_id |
514| | - method_id |
515| | |
516+-------------------+-------------------------------------+
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700517
Wyatt Hepler0f262352020-07-29 09:51:27 -0700518**Errors**
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700519
Wyatt Hepler0f262352020-07-29 09:51:27 -0700520The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet
521it did not request. If the RPC is a streaming RPC, the server should abort it.
522
Wyatt Heplera9211162021-06-12 15:40:11 -0700523The status code indicates the type of error. The status code is logged, but all
524status codes result in the same action by the server: aborting the RPC.
Wyatt Hepler0f262352020-07-29 09:51:27 -0700525
526* ``NOT_FOUND`` -- Received a packet for a service method the client does not
527 recognize.
528* ``FAILED_PRECONDITION`` -- Received a packet for a service method that the
529 client did not invoke.
Wyatt Hepler35240da2021-07-21 08:51:22 -0700530* ``DATA_LOSS`` -- Received a corrupt packet for a pending service method.
Wyatt Hepler0f262352020-07-29 09:51:27 -0700531
532Server-to-client packets
533^^^^^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700534+-------------------+-------------------------------------+
535| packet type | description |
536+===================+=====================================+
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700537| RESPONSE | The RPC is complete |
538| | |
539| | .. code-block:: text |
540| | |
541| | - channel_id |
542| | - service_id |
543| | - method_id |
544| | - status |
545| | - payload |
546| | (unary & client streaming only) |
547| | |
548+-------------------+-------------------------------------+
549| SERVER_STREAM | Message in a server stream |
Wyatt Heplera9211162021-06-12 15:40:11 -0700550| | |
551| | .. code-block:: text |
552| | |
553| | - channel_id |
554| | - service_id |
555| | - method_id |
556| | - payload |
Wyatt Heplera9211162021-06-12 15:40:11 -0700557| | |
558+-------------------+-------------------------------------+
559| SERVER_ERROR | Received unexpected packet |
560| | |
561| | .. code-block:: text |
562| | |
563| | - channel_id |
564| | - service_id (if relevant) |
565| | - method_id (if relevant) |
566| | - status |
567| | |
568+-------------------+-------------------------------------+
Wyatt Hepler0f262352020-07-29 09:51:27 -0700569
570**Errors**
571
572The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot
573process. The client should abort any RPC for which it receives an error. The
574status field indicates the type of error.
575
576* ``NOT_FOUND`` -- The requested service or method does not exist.
Wyatt Heplera9211162021-06-12 15:40:11 -0700577* ``FAILED_PRECONDITION`` -- A client stream or cancel packet was sent for an
578 RPC that is not pending.
Wyatt Hepler01fc15b2021-06-10 18:15:59 -0700579* ``INVALID_ARGUMENT`` -- The client sent a packet type to an RPC that does not
580 support it (e.g. a ``CLIENT_STREAM`` was sent to a server streaming RPC).
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700581* ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel
582 could not be allocated for it.
Wyatt Hepler712d3672020-07-13 15:52:11 -0700583* ``INTERNAL`` -- The server was unable to respond to an RPC due to an
584 unrecoverable internal error.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700585
586Inovking a service method
587-------------------------
588Calling an RPC requires a specific sequence of packets. This section describes
589the protocol for calling service methods of each type: unary, server streaming,
590client streaming, and bidirectional streaming.
591
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700592The basic flow for all RPC invocations is as follows:
593
594 * Client sends a ``REQUEST`` packet. Includes a payload for unary & server
595 streaming RPCs.
596 * For client and bidirectional streaming RPCs, the client may send any number
597 of ``CLIENT_STREAM`` packets with payloads.
598 * For server and bidirectional streaming RPCs, the server may send any number
599 of ``SERVER_STREAM`` packets.
600 * The server sends a ``RESPONSE`` packet. Includes a payload for unary &
601 client streaming RPCs. The RPC is complete.
602
603The client may cancel an ongoing RPC at any time by sending a ``CANCEL`` packet.
604The server may finish an ongoing RPC at any time by sending the ``RESPONSE``
605packet.
606
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700607Unary RPC
608^^^^^^^^^
609In a unary RPC, the client sends a single request and the server sends a single
610response.
611
Wyatt Hepler1d221242021-09-07 15:42:21 -0700612.. image:: unary_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700613
Wyatt Heplera9211162021-06-12 15:40:11 -0700614The client may attempt to cancel a unary RPC by sending a ``CANCEL`` packet. The
615server sends no response to a cancelled RPC. If the server processes the unary
616RPC synchronously (the handling thread sends the response), it may not be
617possible to cancel the RPC.
618
Wyatt Hepler1d221242021-09-07 15:42:21 -0700619.. image:: unary_rpc_cancelled.svg
Wyatt Heplera9211162021-06-12 15:40:11 -0700620
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700621Server streaming RPC
622^^^^^^^^^^^^^^^^^^^^
623In a server streaming RPC, the client sends a single request and the server
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700624sends any number of ``SERVER_STREAM`` packets followed by a ``RESPONSE`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700625
Wyatt Hepler1d221242021-09-07 15:42:21 -0700626.. image:: server_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700627
Wyatt Heplera9211162021-06-12 15:40:11 -0700628The client may terminate a server streaming RPC by sending a ``CANCEL`` packet.
629The server sends no response.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700630
Wyatt Hepler1d221242021-09-07 15:42:21 -0700631.. image:: server_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700632
633Client streaming RPC
634^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700635In a client streaming RPC, the client starts the RPC by sending a ``REQUEST``
636packet with no payload. It then sends any number of messages in
637``CLIENT_STREAM`` packets, followed by a ``CLIENT_STREAM_END``. The server sends
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700638a single ``RESPONSE`` to finish the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700639
Wyatt Hepler1d221242021-09-07 15:42:21 -0700640.. image:: client_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700641
Wyatt Heplera9211162021-06-12 15:40:11 -0700642The server may finish the RPC at any time by sending its ``RESPONSE`` packet,
643even if it has not yet received the ``CLIENT_STREAM_END`` packet. The client may
644terminate the RPC at any time by sending a ``CANCEL`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700645
Wyatt Hepler1d221242021-09-07 15:42:21 -0700646.. image:: client_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700647
648Bidirectional streaming RPC
649^^^^^^^^^^^^^^^^^^^^^^^^^^^
650In a bidirectional streaming RPC, the client sends any number of requests and
Wyatt Heplera9211162021-06-12 15:40:11 -0700651the server sends any number of responses. The client invokes the RPC by sending
652a ``REQUEST`` with no payload. It sends a ``CLIENT_STREAM_END`` packet when it
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700653has finished sending requests. The server sends a ``RESPONSE`` packet to finish
654the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700655
Wyatt Hepler1d221242021-09-07 15:42:21 -0700656.. image:: bidirectional_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700657
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700658The server may finish the RPC at any time by sending the ``RESPONSE`` packet,
659even if it has not received the ``CLIENT_STREAM_END`` packet. The client may
660terminate the RPC at any time by sending a ``CANCEL`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700661
Wyatt Hepler1d221242021-09-07 15:42:21 -0700662.. image:: bidirectional_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700663
Wyatt Hepler948f5472020-06-02 16:52:28 -0700664RPC server
665==========
666Declare an instance of ``rpc::Server`` and register services with it.
667
668.. admonition:: TODO
669
670 Document the public interface
671
Alexei Frolovbf33d212020-09-15 17:13:45 -0700672Size report
673-----------
674The following size report showcases the memory usage of the core RPC server. It
675is configured with a single channel using a basic transport interface that
676directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
677packet buffer, which comprises the plurality of the example's RAM usage. This is
678not a suitable transport for an actual product; a real implementation would have
679additional overhead proportional to the complexity of the transport.
680
681.. include:: server_size
682
Wyatt Hepler948f5472020-06-02 16:52:28 -0700683RPC server implementation
684-------------------------
685
686The Method class
687^^^^^^^^^^^^^^^^
688The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
689serves as the bridge between the ``pw_rpc`` server library and the user-defined
Wyatt Heplere95bd722020-11-23 07:49:47 -0800690RPC functions. Each supported protobuf implementation extends ``Method`` to
691implement its request and response proto handling. The ``pw_rpc`` server
692calls into the ``Method`` implementation through the base class's ``Invoke``
693function.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700694
Wyatt Heplere95bd722020-11-23 07:49:47 -0800695``Method`` implementations store metadata about each method, including a
696function pointer to the user-defined method implementation. They also provide
697``static constexpr`` functions for creating each type of method. ``Method``
698implementations must satisfy the ``MethodImplTester`` test class in
Wyatt Heplerfa6edcc2021-08-20 08:30:08 -0700699``pw_rpc/internal/method_impl_tester.h``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700700
Wyatt Heplere95bd722020-11-23 07:49:47 -0800701See ``pw_rpc/internal/method.h`` for more details about ``Method``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700702
703Packet flow
704^^^^^^^^^^^
705
706Requests
707~~~~~~~~
708
Wyatt Hepler1d221242021-09-07 15:42:21 -0700709.. image:: request_packets.svg
Wyatt Hepler948f5472020-06-02 16:52:28 -0700710
711Responses
712~~~~~~~~~
713
Wyatt Hepler1d221242021-09-07 15:42:21 -0700714.. image:: response_packets.svg
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700715
716RPC client
717==========
718The RPC client is used to send requests to a server and manages the contexts of
719ongoing RPCs.
720
721Setting up a client
722-------------------
723The ``pw::rpc::Client`` class is instantiated with a list of channels that it
724uses to communicate. These channels can be shared with a server, but multiple
725clients cannot use the same channels.
726
727To send incoming RPC packets from the transport layer to be processed by a
728client, the client's ``ProcessPacket`` function is called with the packet data.
729
730.. code:: c++
731
732 #include "pw_rpc/client.h"
733
734 namespace {
735
736 pw::rpc::Channel my_channels[] = {
737 pw::rpc::Channel::Create<1>(&my_channel_output)};
738 pw::rpc::Client my_client(my_channels);
739
740 } // namespace
741
742 // Called when the transport layer receives an RPC packet.
743 void ProcessRpcPacket(ConstByteSpan packet) {
744 my_client.ProcessPacket(packet);
745 }
746
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700747.. _module-pw_rpc-making-calls:
748
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700749Making RPC calls
750----------------
751RPC calls are not made directly through the client, but using one of its
752registered channels instead. A service client class is generated from a .proto
753file for each selected protobuf library, which is then used to send RPC requests
754through a given channel. The API for this depends on the protobuf library;
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700755please refer to the
756:ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple
757service client implementations can exist simulatenously and share the same
758``Client`` class.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700759
760When a call is made, a ``pw::rpc::ClientCall`` object is returned to the caller.
761This object tracks the ongoing RPC call, and can be used to manage it. An RPC
762call is only active as long as its ``ClientCall`` object is alive.
763
764.. tip::
765 Use ``std::move`` when passing around ``ClientCall`` objects to keep RPCs
766 alive.
767
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700768Example
769^^^^^^^
770.. code-block:: c++
771
772 #include "pw_rpc/echo_service_nanopb.h"
773
774 namespace {
Alexei Frolov2b54ee62021-04-29 14:58:21 -0700775 // Generated clients are namespaced with their proto library.
776 using pw::rpc::nanopb::EchoServiceClient;
777
Alexei Frolov73687fb2021-09-03 11:22:33 -0700778 // RPC channel ID on which to make client calls.
779 constexpr uint32_t kDefaultChannelId = 1;
780
Alexei Frolov2b54ee62021-04-29 14:58:21 -0700781 EchoServiceClient::EchoCall echo_call;
Alexei Frolovbebba902021-06-09 17:03:52 -0700782
783 // Callback invoked when a response is received. This is called synchronously
784 // from Client::ProcessPacket.
785 void EchoResponse(const pw_rpc_EchoMessage& response,
786 pw::Status status) {
787 if (status.ok()) {
788 PW_LOG_INFO("Received echo response: %s", response.msg);
789 } else {
790 PW_LOG_ERROR("Echo failed with status %d",
791 static_cast<int>(status.code()));
792 }
793 }
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700794
795 } // namespace
796
797 void CallEcho(const char* message) {
Alexei Frolov73687fb2021-09-03 11:22:33 -0700798 // Create a client to call the EchoService.
799 EchoServiceClient echo_client(my_rpc_client, kDefaultChannelId);
800
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700801 pw_rpc_EchoMessage request = pw_rpc_EchoMessage_init_default;
802 pw::string::Copy(message, request.msg);
803
804 // By assigning the returned ClientCall to the global echo_call, the RPC
805 // call is kept alive until it completes. When a response is received, it
806 // will be logged by the handler function and the call will complete.
Alexei Frolov73687fb2021-09-03 11:22:33 -0700807 echo_call = echo_client.Echo(request, EchoResponse);
808 if (!echo_call.active()) {
809 // The RPC call was not sent. This could occur due to, for example, an
810 // invalid channel ID. Handle if necessary.
811 }
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700812 }
813
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700814Client implementation details
815-----------------------------
816
817The ClientCall class
818^^^^^^^^^^^^^^^^^^^^
819``ClientCall`` stores the context of an active RPC, and serves as the user's
820interface to the RPC client. The core RPC library provides a base ``ClientCall``
821class with common functionality, which is then extended for RPC client
822implementations tied to different protobuf libraries to provide convenient
823interfaces for working with RPCs.
824
825The RPC server stores a list of all of active ``ClientCall`` objects. When an
826incoming packet is recieved, it dispatches to one of its active calls, which
827then decodes the payload and presents it to the user.
Alexei Frolov3e280922021-04-12 14:53:06 -0700828
829ClientServer
830============
831Sometimes, a device needs to both process RPCs as a server, as well as making
832calls to another device as a client. To do this, both a client and server must
833be set up, and incoming packets must be sent to both of them.
834
835Pigweed simplifies this setup by providing a ``ClientServer`` class which wraps
836an RPC client and server with the same set of channels.
837
838.. code-block:: cpp
839
840 pw::rpc::Channel channels[] = {
841 pw::rpc::Channel::Create<1>(&channel_output)};
842
843 // Creates both a client and a server.
844 pw::rpc::ClientServer client_server(channels);
845
846 void ProcessRpcData(pw::ConstByteSpan packet) {
847 // Calls into both the client and the server, sending the packet to the
848 // appropriate one.
849 client_server.ProcessPacket(packet, output);
850 }