blob: 3948631792f7875e94261938ade0ee3e358f412f [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 Hepler8e756e32021-11-18 09:59:27 -0800210 pw::Status MethodOne(const foo_bar_Request& request,
Wyatt Hepler455b4922020-09-18 00:19:21 -0700211 foo_bar_Response& response) {
212 // implementation
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800213 return pw::OkStatus();
Wyatt Hepler455b4922020-09-18 00:19:21 -0700214 }
215
Wyatt Hepler8e756e32021-11-18 09:59:27 -0800216 void MethodTwo(const foo_bar_Request& request,
Wyatt Hepler455b4922020-09-18 00:19:21 -0700217 ServerWriter<foo_bar_Response>& response) {
218 // implementation
219 response.Write(foo_bar_Response{.number = 123});
220 }
221 };
222
223 } // namespace foo::bar
224
225The Nanopb implementation would be declared in a ``BUILD.gn``:
226
227.. code-block:: python
228
229 import("//build_overrides/pigweed.gni")
230
231 import("$dir_pw_build/target_types.gni")
232
233 pw_source_set("the_service") {
234 public_configs = [ ":public" ]
235 public = [ "public/foo_bar/service.h" ]
Wyatt Hepler8779bcd2020-11-25 07:25:16 -0800236 public_deps = [ ":the_service_proto.nanopb_rpc" ]
Wyatt Hepler455b4922020-09-18 00:19:21 -0700237 }
238
239.. attention::
240
241 pw_rpc's generated classes will support using ``pw_protobuf`` or raw buffers
242 (no protobuf library) in the future.
243
Wyatt Hepler8779bcd2020-11-25 07:25:16 -08002444. Register the service with a server
Wyatt Hepler455b4922020-09-18 00:19:21 -0700245-------------------------------------
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800246This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc>`
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700247channel output and the example service.
Wyatt Hepler455b4922020-09-18 00:19:21 -0700248
249.. code-block:: cpp
250
251 // Set up the output channel for the pw_rpc server to use. This configures the
252 // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
253 // adapt this as necessary.
254 pw::stream::SysIoWriter writer;
255 pw::rpc::RpcChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800256 writer, pw::hdlc::kDefaultRpcAddress, "HDLC output");
Wyatt Hepler455b4922020-09-18 00:19:21 -0700257
258 pw::rpc::Channel channels[] = {
259 pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
260
261 // Declare the pw_rpc server with the HDLC channel.
262 pw::rpc::Server server(channels);
263
264 pw::rpc::TheService the_service;
265
266 void RegisterServices() {
267 // Register the foo.bar.TheService example service.
268 server.Register(the_service);
269
270 // Register other services
271 }
272
273 int main() {
274 // Set up the server.
275 RegisterServices();
276
277 // Declare a buffer for decoding incoming HDLC frames.
278 std::array<std::byte, kMaxTransmissionUnit> input_buffer;
279
280 PW_LOG_INFO("Starting pw_rpc server");
Alexei Frolovd3e5cb72021-01-08 13:08:45 -0800281 pw::hdlc::ReadAndProcessPackets(
Wyatt Hepler455b4922020-09-18 00:19:21 -0700282 server, hdlc_channel_output, input_buffer);
283 }
Wyatt Hepler948f5472020-06-02 16:52:28 -0700284
Alexei Frolov1c670a22021-04-09 10:18:17 -0700285Channels
286========
287``pw_rpc`` sends all of its packets over channels. These are logical,
288application-layer routes used to tell the RPC system where a packet should go.
289
290Channels over a client-server connection must all have a unique ID, which can be
291assigned statically at compile time or dynamically.
292
293.. code-block:: cpp
294
295 // Creating a channel with the static ID 3.
296 pw::rpc::Channel static_channel = pw::rpc::Channel::Create<3>(&output);
297
298 // Grouping channel IDs within an enum can lead to clearer code.
299 enum ChannelId {
300 kUartChannel = 1,
301 kSpiChannel = 2,
302 };
303
304 // Creating a channel with a static ID defined within an enum.
305 pw::rpc::Channel another_static_channel =
306 pw::rpc::Channel::Create<ChannelId::kUartChannel>(&output);
307
308 // Creating a channel with a dynamic ID (note that no output is provided; it
309 // will be set when the channel is used.
310 pw::rpc::Channel dynamic_channel;
311
Alexei Frolov567e6702021-04-13 09:13:02 -0700312Sometimes, the ID and output of a channel are not known at compile time as they
313depend on information stored on the physical device. To support this use case, a
314dynamically-assignable channel can be configured once at runtime with an ID and
315output.
316
317.. code-block:: cpp
318
319 // Create a dynamic channel without a compile-time ID or output.
320 pw::rpc::Channel dynamic_channel;
321
322 void Init() {
323 // Called during boot to pull the channel configuration from the system.
324 dynamic_channel.Configure(GetChannelId(), some_output);
325 }
326
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700327Services
328========
329A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc``
330uses these .proto definitions to generate code for a base service, from which
331user-defined RPCs are implemented.
332
333``pw_rpc`` supports multiple protobuf libraries, and the generated code API
334depends on which is used.
335
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700336.. _module-pw_rpc-protobuf-library-apis:
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700337
338Protobuf library APIs
339=====================
340
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700341.. toctree::
342 :maxdepth: 1
343
344 nanopb/docs
345
346Testing a pw_rpc integration
347============================
348After setting up a ``pw_rpc`` server in your project, you can test that it is
349working as intended by registering the provided ``EchoService``, defined in
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800350``echo.proto``, which echoes back a message that it receives.
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700351
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800352.. literalinclude:: echo.proto
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700353 :language: protobuf
354 :lines: 14-
355
356For example, in C++ with nanopb:
357
358.. code:: c++
359
360 #include "pw_rpc/server.h"
361
362 // Include the apporpriate header for your protobuf library.
363 #include "pw_rpc/echo_service_nanopb.h"
364
365 constexpr pw::rpc::Channel kChannels[] = { /* ... */ };
366 static pw::rpc::Server server(kChannels);
367
368 static pw::rpc::EchoService echo_service;
369
370 void Init() {
Wyatt Hepler31b16ea2021-07-21 08:52:02 -0700371 server.RegisterService(echo_service);
Alexei Frolov7c7a3862020-07-16 15:36:02 -0700372 }
373
Wyatt Hepler31b16ea2021-07-21 08:52:02 -0700374Benchmarking and stress testing
375-------------------------------
376
377.. toctree::
378 :maxdepth: 1
379 :hidden:
380
381 benchmark
382
383``pw_rpc`` provides an RPC service and Python module for stress testing and
384benchmarking a ``pw_rpc`` deployment. See :ref:`module-pw_rpc-benchmark`.
385
Wyatt Hepler05d860d2021-09-22 09:43:10 -0700386Naming
387======
388
389Reserved names
390--------------
391``pw_rpc`` reserves a few service method names so they can be used for generated
392classes. The following names cannnot be used for service methods:
393
394- ``Client``
395- ``Service``
396- Any reserved words in the languages ``pw_rpc`` supports (e.g. ``class``).
397
398``pw_rpc`` does not reserve any service names, but the restriction of avoiding
399reserved words in supported languages applies.
400
401Service naming style
402--------------------
403``pw_rpc`` service names should use capitalized camel case and should not use
404the term "Service". Appending "Service" to a service name is redundant, similar
405to appending "Class" or "Function" to a class or function name. The
406C++ implementation class may use "Service" in its name, however.
407
408For example, a service for accessing a file system should simply be named
409``service FileSystem``, rather than ``service FileSystemService``, in the
410``.proto`` file.
411
412.. code-block:: protobuf
413
414 // file.proto
415 package pw.file;
416
417 service FileSystem {
418 rpc List(ListRequest) returns (stream ListResponse);
419 }
420
421The C++ service implementation class may append "Service" to the name.
422
423.. code-block:: cpp
424
425 // file_system_service.h
426 #include "pw_file/file.raw_rpc.pb.h"
427
428 namespace pw::file {
429
430 class FileSystemService : public pw_rpc::raw::FileSystem::Service<FileSystemService> {
Wyatt Hepler8e756e32021-11-18 09:59:27 -0800431 void List(ConstByteSpan request, RawServerWriter& writer);
Wyatt Hepler05d860d2021-09-22 09:43:10 -0700432 };
433
434 }
435
436For upstream Pigweed services, this naming style is a requirement. Note that
437some services created before this was established may use non-compliant
438names. For Pigweed users, this naming style is a suggestion.
439
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700440Protocol description
441====================
442Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These
443packets are used to send requests and responses, control streams, cancel ongoing
444RPCs, and report errors.
445
446Packet format
447-------------
448Pigweed RPC packets consist of a type and a set of fields. The packets are
449encoded as protocol buffers. The full packet format is described in
Wyatt Heplerba325e42021-03-08 14:23:34 -0800450``pw_rpc/pw_rpc/internal/packet.proto``.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700451
Wyatt Hepler752d7d32021-03-02 09:02:23 -0800452.. literalinclude:: internal/packet.proto
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700453 :language: protobuf
454 :lines: 14-
455
456The packet type and RPC type determine which fields are present in a Pigweed RPC
Wyatt Hepler0f262352020-07-29 09:51:27 -0700457packet. Each packet type is only sent by either the client or the server.
458These tables describe the meaning of and fields included with each packet type.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700459
Wyatt Hepler0f262352020-07-29 09:51:27 -0700460Client-to-server packets
461^^^^^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700462+-------------------+-------------------------------------+
463| packet type | description |
464+===================+=====================================+
465| REQUEST | Invoke an RPC |
466| | |
467| | .. code-block:: text |
468| | |
469| | - channel_id |
470| | - service_id |
471| | - method_id |
472| | - payload |
473| | (unary & server streaming only) |
Alexei Frolov86e05de2021-10-19 16:52:31 -0700474| | - call_id (optional) |
Wyatt Heplera9211162021-06-12 15:40:11 -0700475| | |
476+-------------------+-------------------------------------+
477| CLIENT_STREAM | Message in a client stream |
478| | |
479| | .. code-block:: text |
480| | |
481| | - channel_id |
482| | - service_id |
483| | - method_id |
484| | - payload |
Alexei Frolov86e05de2021-10-19 16:52:31 -0700485| | - call_id (if set in REQUEST) |
Wyatt Heplera9211162021-06-12 15:40:11 -0700486| | |
487+-------------------+-------------------------------------+
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700488| CLIENT_STREAM_END | Client stream is complete |
489| | |
490| | .. code-block:: text |
491| | |
492| | - channel_id |
493| | - service_id |
494| | - method_id |
Alexei Frolov86e05de2021-10-19 16:52:31 -0700495| | - call_id (if set in REQUEST) |
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700496| | |
497+-------------------+-------------------------------------+
Wyatt Hepler17169152021-10-20 18:46:08 -0700498| CLIENT_ERROR | Abort an ongoing RPC |
Wyatt Heplera9211162021-06-12 15:40:11 -0700499| | |
500| | .. code-block:: text |
501| | |
502| | - channel_id |
503| | - service_id |
504| | - method_id |
505| | - status |
Alexei Frolov86e05de2021-10-19 16:52:31 -0700506| | - call_id (if set in REQUEST) |
Wyatt Heplera9211162021-06-12 15:40:11 -0700507| | |
508+-------------------+-------------------------------------+
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700509
Wyatt Hepler82db4b12021-09-23 09:10:12 -0700510**Client errors**
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700511
Wyatt Hepler0f262352020-07-29 09:51:27 -0700512The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet
Wyatt Hepler82db4b12021-09-23 09:10:12 -0700513it did not request. If possible, the server should abort it.
Wyatt Hepler0f262352020-07-29 09:51:27 -0700514
Wyatt Heplera9211162021-06-12 15:40:11 -0700515The status code indicates the type of error. The status code is logged, but all
516status codes result in the same action by the server: aborting the RPC.
Wyatt Hepler0f262352020-07-29 09:51:27 -0700517
Wyatt Hepler17169152021-10-20 18:46:08 -0700518* ``CANCELLED`` -- The client requested that the RPC be cancelled.
Wyatt Hepler0f262352020-07-29 09:51:27 -0700519* ``NOT_FOUND`` -- Received a packet for a service method the client does not
520 recognize.
521* ``FAILED_PRECONDITION`` -- Received a packet for a service method that the
522 client did not invoke.
Wyatt Hepler35240da2021-07-21 08:51:22 -0700523* ``DATA_LOSS`` -- Received a corrupt packet for a pending service method.
Wyatt Hepler82db4b12021-09-23 09:10:12 -0700524* ``INVALID_ARGUMENT`` -- The server sent a packet type to an RPC that does not
525 support it (a ``SERVER_STREAM`` was sent to an RPC with no server stream).
Wyatt Hepler0f262352020-07-29 09:51:27 -0700526
527Server-to-client packets
528^^^^^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700529+-------------------+-------------------------------------+
530| packet type | description |
531+===================+=====================================+
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700532| RESPONSE | The RPC is complete |
533| | |
534| | .. code-block:: text |
535| | |
536| | - channel_id |
537| | - service_id |
538| | - method_id |
539| | - status |
540| | - payload |
541| | (unary & client streaming only) |
Alexei Frolov86e05de2021-10-19 16:52:31 -0700542| | - call_id (if set in REQUEST) |
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700543| | |
544+-------------------+-------------------------------------+
545| SERVER_STREAM | Message in a server stream |
Wyatt Heplera9211162021-06-12 15:40:11 -0700546| | |
547| | .. code-block:: text |
548| | |
549| | - channel_id |
550| | - service_id |
551| | - method_id |
552| | - payload |
Alexei Frolov86e05de2021-10-19 16:52:31 -0700553| | - call_id (if set in REQUEST) |
Wyatt Heplera9211162021-06-12 15:40:11 -0700554| | |
555+-------------------+-------------------------------------+
556| SERVER_ERROR | Received unexpected packet |
557| | |
558| | .. code-block:: text |
559| | |
560| | - channel_id |
561| | - service_id (if relevant) |
562| | - method_id (if relevant) |
563| | - status |
Alexei Frolov86e05de2021-10-19 16:52:31 -0700564| | - call_id (if set in REQUEST) |
Wyatt Heplera9211162021-06-12 15:40:11 -0700565| | |
566+-------------------+-------------------------------------+
Wyatt Hepler0f262352020-07-29 09:51:27 -0700567
Alexei Frolov86e05de2021-10-19 16:52:31 -0700568All server packets contain the same client ID that was set in the initial
569request made by the client, if any.
570
Wyatt Hepler82db4b12021-09-23 09:10:12 -0700571**Server errors**
Wyatt Hepler0f262352020-07-29 09:51:27 -0700572
573The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot
574process. The client should abort any RPC for which it receives an error. The
575status field indicates the type of error.
576
577* ``NOT_FOUND`` -- The requested service or method does not exist.
Wyatt Heplera9211162021-06-12 15:40:11 -0700578* ``FAILED_PRECONDITION`` -- A client stream or cancel packet was sent for an
579 RPC that is not pending.
Wyatt Hepler01fc15b2021-06-10 18:15:59 -0700580* ``INVALID_ARGUMENT`` -- The client sent a packet type to an RPC that does not
Wyatt Hepler82db4b12021-09-23 09:10:12 -0700581 support it (a ``CLIENT_STREAM`` was sent to an RPC with no client stream).
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700582* ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel
583 could not be allocated for it.
Wyatt Hepler712d3672020-07-13 15:52:11 -0700584* ``INTERNAL`` -- The server was unable to respond to an RPC due to an
585 unrecoverable internal error.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700586
587Inovking a service method
588-------------------------
589Calling an RPC requires a specific sequence of packets. This section describes
590the protocol for calling service methods of each type: unary, server streaming,
591client streaming, and bidirectional streaming.
592
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700593The basic flow for all RPC invocations is as follows:
594
595 * Client sends a ``REQUEST`` packet. Includes a payload for unary & server
596 streaming RPCs.
597 * For client and bidirectional streaming RPCs, the client may send any number
598 of ``CLIENT_STREAM`` packets with payloads.
599 * For server and bidirectional streaming RPCs, the server may send any number
600 of ``SERVER_STREAM`` packets.
601 * The server sends a ``RESPONSE`` packet. Includes a payload for unary &
602 client streaming RPCs. The RPC is complete.
603
Wyatt Hepler17169152021-10-20 18:46:08 -0700604The client may cancel an ongoing RPC at any time by sending a ``CLIENT_ERROR``
605packet with status ``CANCELLED``. The server may finish an ongoing RPC at any
606time by sending the ``RESPONSE`` packet.
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700607
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700608Unary RPC
609^^^^^^^^^
610In a unary RPC, the client sends a single request and the server sends a single
611response.
612
Wyatt Hepler1d221242021-09-07 15:42:21 -0700613.. image:: unary_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700614
Wyatt Hepler17169152021-10-20 18:46:08 -0700615The client may attempt to cancel a unary RPC by sending a ``CLIENT_ERROR``
616packet with status ``CANCELLED``. The server sends no response to a cancelled
617RPC. If the server processes the unary RPC synchronously (the handling thread
618sends the response), it may not be possible to cancel the RPC.
Wyatt Heplera9211162021-06-12 15:40:11 -0700619
Wyatt Hepler1d221242021-09-07 15:42:21 -0700620.. image:: unary_rpc_cancelled.svg
Wyatt Heplera9211162021-06-12 15:40:11 -0700621
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700622Server streaming RPC
623^^^^^^^^^^^^^^^^^^^^
624In a server streaming RPC, the client sends a single request and the server
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700625sends any number of ``SERVER_STREAM`` packets followed by a ``RESPONSE`` packet.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700626
Wyatt Hepler1d221242021-09-07 15:42:21 -0700627.. image:: server_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700628
Wyatt Hepler17169152021-10-20 18:46:08 -0700629The client may terminate a server streaming RPC by sending a ``CLIENT_STREAM``
630packet with status ``CANCELLED``. The server sends no response.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700631
Wyatt Hepler1d221242021-09-07 15:42:21 -0700632.. image:: server_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700633
634Client streaming RPC
635^^^^^^^^^^^^^^^^^^^^
Wyatt Heplera9211162021-06-12 15:40:11 -0700636In a client streaming RPC, the client starts the RPC by sending a ``REQUEST``
637packet with no payload. It then sends any number of messages in
638``CLIENT_STREAM`` packets, followed by a ``CLIENT_STREAM_END``. The server sends
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700639a single ``RESPONSE`` to finish the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700640
Wyatt Hepler1d221242021-09-07 15:42:21 -0700641.. image:: client_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700642
Wyatt Heplera9211162021-06-12 15:40:11 -0700643The server may finish the RPC at any time by sending its ``RESPONSE`` packet,
644even if it has not yet received the ``CLIENT_STREAM_END`` packet. The client may
Wyatt Hepler17169152021-10-20 18:46:08 -0700645terminate the RPC at any time by sending a ``CLIENT_ERROR`` packet with status
646``CANCELLED``.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700647
Wyatt Hepler1d221242021-09-07 15:42:21 -0700648.. image:: client_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700649
650Bidirectional streaming RPC
651^^^^^^^^^^^^^^^^^^^^^^^^^^^
652In a bidirectional streaming RPC, the client sends any number of requests and
Wyatt Heplera9211162021-06-12 15:40:11 -0700653the server sends any number of responses. The client invokes the RPC by sending
654a ``REQUEST`` with no payload. It sends a ``CLIENT_STREAM_END`` packet when it
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700655has finished sending requests. The server sends a ``RESPONSE`` packet to finish
656the RPC.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700657
Wyatt Hepler1d221242021-09-07 15:42:21 -0700658.. image:: bidirectional_streaming_rpc.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700659
Wyatt Hepler5ba80642021-06-18 12:56:17 -0700660The server may finish the RPC at any time by sending the ``RESPONSE`` packet,
661even if it has not received the ``CLIENT_STREAM_END`` packet. The client may
Wyatt Hepler17169152021-10-20 18:46:08 -0700662terminate the RPC at any time by sending a ``CLIENT_ERROR`` packet with status
663``CANCELLED``.
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700664
Wyatt Hepler1d221242021-09-07 15:42:21 -0700665.. image:: bidirectional_streaming_rpc_cancelled.svg
Wyatt Hepler067dd7e2020-07-14 19:34:32 -0700666
Wyatt Hepler948f5472020-06-02 16:52:28 -0700667RPC server
668==========
669Declare an instance of ``rpc::Server`` and register services with it.
670
671.. admonition:: TODO
672
673 Document the public interface
674
Alexei Frolovbf33d212020-09-15 17:13:45 -0700675Size report
676-----------
677The following size report showcases the memory usage of the core RPC server. It
678is configured with a single channel using a basic transport interface that
679directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
680packet buffer, which comprises the plurality of the example's RAM usage. This is
681not a suitable transport for an actual product; a real implementation would have
682additional overhead proportional to the complexity of the transport.
683
684.. include:: server_size
685
Wyatt Hepler948f5472020-06-02 16:52:28 -0700686RPC server implementation
687-------------------------
688
689The Method class
690^^^^^^^^^^^^^^^^
691The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
692serves as the bridge between the ``pw_rpc`` server library and the user-defined
Wyatt Heplere95bd722020-11-23 07:49:47 -0800693RPC functions. Each supported protobuf implementation extends ``Method`` to
694implement its request and response proto handling. The ``pw_rpc`` server
695calls into the ``Method`` implementation through the base class's ``Invoke``
696function.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700697
Wyatt Heplere95bd722020-11-23 07:49:47 -0800698``Method`` implementations store metadata about each method, including a
699function pointer to the user-defined method implementation. They also provide
700``static constexpr`` functions for creating each type of method. ``Method``
701implementations must satisfy the ``MethodImplTester`` test class in
Wyatt Heplerfa6edcc2021-08-20 08:30:08 -0700702``pw_rpc/internal/method_impl_tester.h``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700703
Wyatt Heplere95bd722020-11-23 07:49:47 -0800704See ``pw_rpc/internal/method.h`` for more details about ``Method``.
Wyatt Hepler948f5472020-06-02 16:52:28 -0700705
706Packet flow
707^^^^^^^^^^^
708
709Requests
710~~~~~~~~
711
Wyatt Hepler1d221242021-09-07 15:42:21 -0700712.. image:: request_packets.svg
Wyatt Hepler948f5472020-06-02 16:52:28 -0700713
714Responses
715~~~~~~~~~
716
Wyatt Hepler1d221242021-09-07 15:42:21 -0700717.. image:: response_packets.svg
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700718
719RPC client
720==========
721The RPC client is used to send requests to a server and manages the contexts of
722ongoing RPCs.
723
724Setting up a client
725-------------------
726The ``pw::rpc::Client`` class is instantiated with a list of channels that it
727uses to communicate. These channels can be shared with a server, but multiple
728clients cannot use the same channels.
729
730To send incoming RPC packets from the transport layer to be processed by a
731client, the client's ``ProcessPacket`` function is called with the packet data.
732
733.. code:: c++
734
735 #include "pw_rpc/client.h"
736
737 namespace {
738
739 pw::rpc::Channel my_channels[] = {
740 pw::rpc::Channel::Create<1>(&my_channel_output)};
741 pw::rpc::Client my_client(my_channels);
742
743 } // namespace
744
745 // Called when the transport layer receives an RPC packet.
746 void ProcessRpcPacket(ConstByteSpan packet) {
747 my_client.ProcessPacket(packet);
748 }
749
Alexei Frolov5a3a61c2020-10-01 18:51:41 -0700750.. _module-pw_rpc-making-calls:
751
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700752Making RPC calls
753----------------
754RPC calls are not made directly through the client, but using one of its
755registered channels instead. A service client class is generated from a .proto
756file for each selected protobuf library, which is then used to send RPC requests
757through a given channel. The API for this depends on the protobuf library;
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -0700758please refer to the
759:ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple
760service client implementations can exist simulatenously and share the same
761``Client`` class.
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700762
763When a call is made, a ``pw::rpc::ClientCall`` object is returned to the caller.
764This object tracks the ongoing RPC call, and can be used to manage it. An RPC
765call is only active as long as its ``ClientCall`` object is alive.
766
767.. tip::
768 Use ``std::move`` when passing around ``ClientCall`` objects to keep RPCs
769 alive.
770
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700771Example
772^^^^^^^
773.. code-block:: c++
774
775 #include "pw_rpc/echo_service_nanopb.h"
776
777 namespace {
Alexei Frolov2b54ee62021-04-29 14:58:21 -0700778 // Generated clients are namespaced with their proto library.
Alexei Frolov39d8c5c2021-11-24 10:16:31 -0800779 using EchoClient = pw_rpc::nanopb::EchoService::Client;
Alexei Frolov2b54ee62021-04-29 14:58:21 -0700780
Alexei Frolov73687fb2021-09-03 11:22:33 -0700781 // RPC channel ID on which to make client calls.
782 constexpr uint32_t kDefaultChannelId = 1;
783
Alexei Frolov39d8c5c2021-11-24 10:16:31 -0800784 EchoClient::EchoCall echo_call;
Alexei Frolovbebba902021-06-09 17:03:52 -0700785
786 // Callback invoked when a response is received. This is called synchronously
787 // from Client::ProcessPacket.
788 void EchoResponse(const pw_rpc_EchoMessage& response,
789 pw::Status status) {
790 if (status.ok()) {
791 PW_LOG_INFO("Received echo response: %s", response.msg);
792 } else {
793 PW_LOG_ERROR("Echo failed with status %d",
794 static_cast<int>(status.code()));
795 }
796 }
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700797
798 } // namespace
799
800 void CallEcho(const char* message) {
Alexei Frolov73687fb2021-09-03 11:22:33 -0700801 // Create a client to call the EchoService.
Alexei Frolov39d8c5c2021-11-24 10:16:31 -0800802 EchoClient echo_client(my_rpc_client, kDefaultChannelId);
Alexei Frolov73687fb2021-09-03 11:22:33 -0700803
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700804 pw_rpc_EchoMessage request = pw_rpc_EchoMessage_init_default;
805 pw::string::Copy(message, request.msg);
806
807 // By assigning the returned ClientCall to the global echo_call, the RPC
808 // call is kept alive until it completes. When a response is received, it
809 // will be logged by the handler function and the call will complete.
Alexei Frolov73687fb2021-09-03 11:22:33 -0700810 echo_call = echo_client.Echo(request, EchoResponse);
811 if (!echo_call.active()) {
812 // The RPC call was not sent. This could occur due to, for example, an
813 // invalid channel ID. Handle if necessary.
814 }
Alexei Frolov2d737bc2021-04-27 23:03:09 -0700815 }
816
Alexei Frolov4d2adde2020-08-04 10:19:24 -0700817Client implementation details
818-----------------------------
819
820The ClientCall class
821^^^^^^^^^^^^^^^^^^^^
822``ClientCall`` stores the context of an active RPC, and serves as the user's
823interface to the RPC client. The core RPC library provides a base ``ClientCall``
824class with common functionality, which is then extended for RPC client
825implementations tied to different protobuf libraries to provide convenient
826interfaces for working with RPCs.
827
828The RPC server stores a list of all of active ``ClientCall`` objects. When an
829incoming packet is recieved, it dispatches to one of its active calls, which
830then decodes the payload and presents it to the user.
Alexei Frolov3e280922021-04-12 14:53:06 -0700831
832ClientServer
833============
834Sometimes, a device needs to both process RPCs as a server, as well as making
835calls to another device as a client. To do this, both a client and server must
836be set up, and incoming packets must be sent to both of them.
837
838Pigweed simplifies this setup by providing a ``ClientServer`` class which wraps
839an RPC client and server with the same set of channels.
840
841.. code-block:: cpp
842
843 pw::rpc::Channel channels[] = {
844 pw::rpc::Channel::Create<1>(&channel_output)};
845
846 // Creates both a client and a server.
847 pw::rpc::ClientServer client_server(channels);
848
849 void ProcessRpcData(pw::ConstByteSpan packet) {
850 // Calls into both the client and the server, sending the packet to the
851 // appropriate one.
852 client_server.ProcessPacket(packet, output);
853 }
Wyatt Hepler9732a8e2021-11-10 16:31:41 -0800854
855Unit testing
856============
857``pw_rpc`` provides utilities for unit testing RPC services and client calls.
858
859Client testing in C++
860---------------------
861``pw_rpc`` supports invoking RPCs, simulating server responses, and checking
862what packets are sent by an RPC client in tests. Both raw and Nanopb interfaces
863are supported. Code that uses the raw API may be tested with the Nanopb test
864helpers, and vice versa.
865
866To test code that invokes RPCs, declare a ``RawClientTestContext`` or
867``NanopbClientTestContext``. These test context objects provide a
868preconfigured RPC client, channel, server fake, and buffer for encoding packets.
869These test classes are defined in ``pw_rpc/raw/client_testing.h`` and
870``pw_rpc/nanopb/client_testing.h``.
871
872Use the context's ``client()`` and ``channel()`` to invoke RPCs. Use the
873context's ``server()`` to simulate responses. To verify that the client sent the
874expected data, use the context's ``output()``, which is a ``FakeChannelOutput``.
875
876For example, the following tests a class that invokes an RPC. It checks that
877the expected data was sent and then simulates a response from the server.
878
879.. code-block:: cpp
880
881 #include "pw_rpc/raw/client_testing.h"
882
883 class ThingThatCallsRpcs {
884 public:
885 // To support injecting an RPC client for testing, classes that make RPC
886 // calls should take an RPC client and channel ID or an RPC service client
887 // (e.g. pw_rpc::raw::MyService::Client).
888 ThingThatCallsRpcs(pw::rpc::Client& client, uint32_t channel_id);
889
890 void DoSomethingThatInvokesAnRpc();
891
892 bool SetToTrueWhenRpcCompletes();
893 };
894
895 TEST(TestAThing, InvokesRpcAndHandlesResponse) {
896 RawClientTestContext context;
897 ThingThatCallsRpcs thing(context.client(), context.channel().id());
898
899 // Execute the code that invokes the MyService.TheMethod RPC.
900 things.DoSomethingThatInvokesAnRpc();
901
902 // Find and verify the payloads sent for the MyService.TheMethod RPC.
903 auto msgs = context.output().payloads<pw_rpc::raw::MyService::TheMethod>();
904 ASSERT_EQ(msgs.size(), 1u);
905
906 VerifyThatTheExpectedMessageWasSent(msgs.back());
907
908 // Send the response packet from the server and verify that the class reacts
909 // accordingly.
910 EXPECT_FALSE(thing.SetToTrueWhenRpcCompletes());
911
912 context_.server().SendResponse<pw_rpc::raw::MyService::TheMethod>(
913 final_message, OkStatus());
914
915 EXPECT_TRUE(thing.SetToTrueWhenRpcCompletes());
916 }
Ewout van Bekkum7f5b3052021-11-11 17:35:23 -0800917
918Module Configuration Options
919============================
920The following configurations can be adjusted via compile-time configuration of
921this module, see the
922:ref:`module documentation <module-structure-compile-time-configuration>` for
923more details.
924
925.. c:macro:: PW_RPC_CLIENT_STREAM_END_CALLBACK
926
927 In client and bidirectional RPCs, pw_rpc clients may signal that they have
928 finished sending requests with a CLIENT_STREAM_END packet. While this can be
929 useful in some circumstances, it is often not necessary.
930
931 This option controls whether or not include a callback that is called when
932 the client stream ends. The callback is included in all ServerReader/Writer
933 objects as a pw::Function, so may have a significant cost.
934
935 This is disabled by default.
936
937.. c:macro:: PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE
938
939 The Nanopb-based pw_rpc implementation allocates memory to use for Nanopb
940 structs for the request and response protobufs. The template function that
941 allocates these structs rounds struct sizes up to this value so that
942 different structs can be allocated with the same function. Structs with sizes
943 larger than this value cause an extra function to be created, which slightly
944 increases code size.
945
946 Ideally, this value will be set to the size of the largest Nanopb struct used
947 as an RPC request or response. The buffer can be stack or globally allocated
948 (see ``PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE``).
949
950 This defaults to 64 Bytes.
951
952.. c:macro:: PW_RPC_USE_GLOBAL_MUTEX
953
954 Enable global synchronization for RPC calls. If this is set, a backend must
955 be configured for pw_sync:mutex.
956
957 This is disabled by default.
958
959.. c:macro:: PW_RPC_CONFIG_LOG_LEVEL
960
961 The log level to use for this module. Logs below this level are omitted.
962
963 This defaults to ``PW_LOG_LEVEL_INFO``.
964
965.. c:macro:: PW_RPC_CONFIG_LOG_MODULE_NAME
966
967 The log module name to use for this module.
968
969 This defaults to ``"PW_RPC"``.
970
971.. c:macro:: PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE
972
973 This option determines whether to allocate the Nanopb structs on the stack or
974 in a global variable. Globally allocated structs are NOT thread safe, but
975 work fine when the RPC server's ProcessPacket function is only called from
976 one thread.
977
978 This is enabled by default.
Wyatt Hepler14439762021-11-09 09:16:32 -0800979
980Sharing server and client code
981==============================
982Streaming RPCs support writing multiple requests or responses. To facilitate
983sharing code between servers and clients, ``pw_rpc`` provides the
984``pw::rpc::Writer`` interface. On the client side, a client or bidirectional
985streaming RPC call object (``ClientWriter`` or ``ClientReaderWriter``) can be
986used as a ``pw::rpc::Writer&``. On the server side, a server or bidirectional
987streaming RPC call object (``ServerWriter`` or ``ServerReaderWriter``) can be
988used as a ``pw::rpc::Writer&``.