1.. _module-pw_rpc: 2 3====== 4pw_rpc 5====== 6The ``pw_rpc`` module provides a system for defining and invoking remote 7procedure calls (RPCs) on a device. 8 9This document discusses the ``pw_rpc`` protocol and its C++ implementation. 10``pw_rpc`` implementations for other languages are described in their own 11documents: 12 13.. toctree:: 14 :maxdepth: 1 15 16 py/docs 17 ts/docs 18 19.. admonition:: Try it out! 20 21 For a quick intro to ``pw_rpc``, see the 22 :ref:`module-pw_hdlc-rpc-example` in the :ref:`module-pw_hdlc` module. 23 24.. warning:: 25 26 This documentation is under construction. Many sections are outdated or 27 incomplete. The content needs to be reorgnanized. 28 29--------------- 30Implementations 31--------------- 32Pigweed provides several client and server implementations of ``pw_rpc``. 33 34.. list-table:: 35 :header-rows: 1 36 37 * - Language 38 - Server 39 - Client 40 * - C++ (raw) 41 - ✅ 42 - ✅ 43 * - C++ (Nanopb) 44 - ✅ 45 - ✅ 46 * - C++ (pw_protobuf) 47 - ✅ 48 - ✅ 49 * - Java 50 - 51 - ✅ 52 * - Python 53 - 54 - ✅ 55 * - TypeScript 56 - 57 - in development 58 59------------- 60RPC semantics 61------------- 62The semantics of ``pw_rpc`` are similar to `gRPC 63<https://grpc.io/docs/what-is-grpc/core-concepts/>`_. 64 65RPC call lifecycle 66================== 67In ``pw_rpc``, an RPC begins when the client sends an initial packet. The server 68receives the packet, looks up the relevant service method, then calls into the 69RPC function. The RPC is considered active until the server sends a status to 70finish the RPC. The client may terminate an ongoing RPC by cancelling it. 71 72Depending the type of RPC, the client and server exchange zero or more protobuf 73request or response payloads. There are four RPC types: 74 75* **Unary**. The client sends one request and the server sends one 76 response with a status. 77* **Server streaming**. The client sends one request and the server sends zero 78 or more responses followed by a status. 79* **Client streaming**. The client sends zero or more requests and the server 80 sends one response with a status. 81* **Bidirectional streaming**. The client sends zero or more requests and the 82 server sends zero or more responses followed by a status. 83 84Events 85------ 86The key events in the RPC lifecycle are: 87 88* **Start**. The client initiates the RPC. The server's RPC body executes. 89* **Finish**. The server sends a status and completes the RPC. The client calls 90 a callback. 91* **Request**. The client sends a request protobuf. The server calls a callback 92 when it receives it. In unary and server streaming RPCs, there is only one 93 request and it is handled when the RPC starts. 94* **Response**. The server sends a response protobuf. The client calls a 95 callback when it receives it. In unary and client streaming RPCs, there is 96 only one response and it is handled when the RPC completes. 97* **Error**. The server or client terminates the RPC abnormally with a status. 98 The receiving endpoint calls a callback. 99* **Client stream end**. The client sends a message that it has finished sending 100 requests. The server calls a callback when it receives it. Some servers may 101 ignore the client stream end. The client stream end is only relevant for 102 client and bidirectional streaming RPCs. 103 104Status codes 105============ 106``pw_rpc`` call objects (``ClientReaderWriter``, ``ServerReaderWriter``, etc.) 107use certain status codes to indicate what occurred. These codes are returned 108from functions like ``Write()`` or ``Finish()``. 109 110* ``OK`` -- The operation succeeded. 111* ``UNAVAILABLE`` -- The channel is not currently registered with the server or 112 client. 113* ``UNKNOWN`` -- Sending a packet failed due to an unrecoverable 114 :cpp:func:`pw::rpc::ChannelOutput::Send` error. 115 116Unrequested responses 117===================== 118``pw_rpc`` supports sending responses to RPCs that have not yet been invoked by 119a client. This is useful in testing and in situations like an RPC that triggers 120reboot. After the reboot, the device opens the writer object and sends its 121response to the client. 122 123The C++ API for opening a server reader/writer takes the generated RPC function 124as a template parameter. The server to use, channel ID, and service instance are 125passed as arguments. The API is the same for all RPC types, except the 126appropriate reader/writer class must be used. 127 128.. code-block:: c++ 129 130 // Open a ServerWriter for a server streaming RPC. 131 auto writer = RawServerWriter::Open<pw_rpc::raw::ServiceName::MethodName>( 132 server, channel_id, service_instance); 133 134 // Send some responses, even though the client has not yet called this RPC. 135 CHECK_OK(writer.Write(encoded_response_1)); 136 CHECK_OK(writer.Write(encoded_response_2)); 137 138 // Finish the RPC. 139 CHECK_OK(writer.Finish(OkStatus())); 140 141--------------- 142Creating an RPC 143--------------- 144 1451. RPC service declaration 146========================== 147Pigweed RPCs are declared in a protocol buffer service definition. 148 149* `Protocol Buffer service documentation 150 <https://developers.google.com/protocol-buffers/docs/proto3#services>`_ 151* `gRPC service definition documentation 152 <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_ 153 154.. code-block:: protobuf 155 156 syntax = "proto3"; 157 158 package foo.bar; 159 160 message Request {} 161 162 message Response { 163 int32 number = 1; 164 } 165 166 service TheService { 167 rpc MethodOne(Request) returns (Response) {} 168 rpc MethodTwo(Request) returns (stream Response) {} 169 } 170 171This protocol buffer is declared in a ``BUILD.gn`` file as follows: 172 173.. code-block:: python 174 175 import("//build_overrides/pigweed.gni") 176 import("$dir_pw_protobuf_compiler/proto.gni") 177 178 pw_proto_library("the_service_proto") { 179 sources = [ "foo_bar/the_service.proto" ] 180 } 181 182.. admonition:: proto2 or proto3 syntax? 183 184 Always use proto3 syntax rather than proto2 for new protocol buffers. Proto2 185 protobufs can be compiled for ``pw_rpc``, but they are not as well supported 186 as proto3. Specifically, ``pw_rpc`` lacks support for non-zero default values 187 in proto2. When using Nanopb with ``pw_rpc``, proto2 response protobufs with 188 non-zero field defaults should be manually initialized to the default struct. 189 190 In the past, proto3 was sometimes avoided because it lacked support for field 191 presence detection. Fortunately, this has been fixed: proto3 now supports 192 ``optional`` fields, which are equivalent to proto2 ``optional`` fields. 193 194 If you need to distinguish between a default-valued field and a missing field, 195 mark the field as ``optional``. The presence of the field can be detected 196 with ``std::optional``, a ``HasField(name)``, or ``has_<field>`` member, 197 depending on the library. 198 199 Optional fields have some overhead --- if using Nanopb, default-valued fields 200 are included in the encoded proto, and the proto structs have a 201 ``has_<field>`` flag for each optional field. Use plain fields if field 202 presence detection is not needed. 203 204 .. code-block:: protobuf 205 206 syntax = "proto3"; 207 208 message MyMessage { 209 // Leaving this field unset is equivalent to setting it to 0. 210 int32 number = 1; 211 212 // Setting this field to 0 is different from leaving it unset. 213 optional int32 other_number = 2; 214 } 215 2162. RPC code generation 217====================== 218``pw_rpc`` generates a C++ header file for each ``.proto`` file. This header is 219generated in the build output directory. Its exact location varies by build 220system and toolchain, but the C++ include path always matches the sources 221declaration in the ``pw_proto_library``. The ``.proto`` extension is replaced 222with an extension corresponding to the protobuf library in use. 223 224================== =============== =============== ============= 225Protobuf libraries Build subtarget Protobuf header pw_rpc header 226================== =============== =============== ============= 227Raw only .raw_rpc (none) .raw_rpc.pb.h 228Nanopb or raw .nanopb_rpc .pb.h .rpc.pb.h 229pw_protobuf or raw .pwpb_rpc .pwpb.h .rpc.pwpb.h 230================== =============== =============== ============= 231 232For example, the generated RPC header for ``"foo_bar/the_service.proto"`` is 233``"foo_bar/the_service.rpc.pb.h"`` for Nanopb or 234``"foo_bar/the_service.raw_rpc.pb.h"`` for raw RPCs. 235 236The generated header defines a base class for each RPC service declared in the 237``.proto`` file. A service named ``TheService`` in package ``foo.bar`` would 238generate the following base class for pw_protobuf: 239 240.. cpp:class:: template <typename Implementation> foo::bar::pw_rpc::pwpb::TheService::Service 241 2423. RPC service definition 243========================= 244The serivce class is implemented by inheriting from the generated RPC service 245base class and defining a method for each RPC. The methods must match the name 246and function signature for one of the supported protobuf implementations. 247Services may mix and match protobuf implementations within one service. 248 249.. tip:: 250 251 The generated code includes RPC service implementation stubs. You can 252 reference or copy and paste these to get started with implementing a service. 253 These stub classes are generated at the bottom of the pw_rpc proto header. 254 255 To use the stubs, do the following: 256 257 #. Locate the generated RPC header in the build directory. For example: 258 259 .. code-block:: sh 260 261 find out/ -name <proto_name>.rpc.pwpb.h 262 263 #. Scroll to the bottom of the generated RPC header. 264 #. Copy the stub class declaration to a header file. 265 #. Copy the member function definitions to a source file. 266 #. Rename the class or change the namespace, if desired. 267 #. List these files in a build target with a dependency on the 268 ``pw_proto_library``. 269 270A pw_protobuf implementation of this service would be as follows: 271 272.. code-block:: cpp 273 274 #include "foo_bar/the_service.rpc.pwpb.h" 275 276 namespace foo::bar { 277 278 class TheService : public pw_rpc::pwpb::TheService::Service<TheService> { 279 public: 280 pw::Status MethodOne(const Request::Message& request, 281 Response::Message& response) { 282 // implementation 283 response.number = 123; 284 return pw::OkStatus(); 285 } 286 287 void MethodTwo(const Request::Message& request, 288 ServerWriter<Response::Message>& response) { 289 // implementation 290 response.Write({.number = 123}); 291 } 292 }; 293 294 } // namespace foo::bar 295 296The pw_protobuf implementation would be declared in a ``BUILD.gn``: 297 298.. code-block:: python 299 300 import("//build_overrides/pigweed.gni") 301 302 import("$dir_pw_build/target_types.gni") 303 304 pw_source_set("the_service") { 305 public_configs = [ ":public" ] 306 public = [ "public/foo_bar/service.h" ] 307 public_deps = [ ":the_service_proto.pwpb_rpc" ] 308 } 309 3104. Register the service with a server 311===================================== 312This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc>` 313channel output and the example service. 314 315.. code-block:: cpp 316 317 // Set up the output channel for the pw_rpc server to use. This configures the 318 // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must 319 // adapt this as necessary. 320 pw::stream::SysIoWriter writer; 321 pw::rpc::FixedMtuChannelOutput<kMaxTransmissionUnit> hdlc_channel_output( 322 writer, pw::hdlc::kDefaultRpcAddress, "HDLC output"); 323 324 // Allocate an array of channels for the server to use. If dynamic allocation 325 // is enabled (PW_RPC_DYNAMIC_ALLOCATION=1), the server can be initialized 326 // without any channels, and they can be added later. 327 pw::rpc::Channel channels[] = { 328 pw::rpc::Channel::Create<1>(&hdlc_channel_output)}; 329 330 // Declare the pw_rpc server with the HDLC channel. 331 pw::rpc::Server server(channels); 332 333 foo::bar::TheService the_service; 334 pw::rpc::SomeOtherService some_other_service; 335 336 void RegisterServices() { 337 // Register the foo.bar.TheService example service and another service. 338 server.RegisterService(the_service, some_other_service); 339 } 340 341 int main() { 342 // Set up the server. 343 RegisterServices(); 344 345 // Declare a buffer for decoding incoming HDLC frames. 346 std::array<std::byte, kMaxTransmissionUnit> input_buffer; 347 348 PW_LOG_INFO("Starting pw_rpc server"); 349 pw::hdlc::ReadAndProcessPackets(server, input_buffer); 350 } 351 352-------- 353Channels 354-------- 355``pw_rpc`` sends all of its packets over channels. These are logical, 356application-layer routes used to tell the RPC system where a packet should go. 357 358Channels over a client-server connection must all have a unique ID, which can be 359assigned statically at compile time or dynamically. 360 361.. code-block:: cpp 362 363 // Creating a channel with the static ID 3. 364 pw::rpc::Channel static_channel = pw::rpc::Channel::Create<3>(&output); 365 366 // Grouping channel IDs within an enum can lead to clearer code. 367 enum ChannelId { 368 kUartChannel = 1, 369 kSpiChannel = 2, 370 }; 371 372 // Creating a channel with a static ID defined within an enum. 373 pw::rpc::Channel another_static_channel = 374 pw::rpc::Channel::Create<ChannelId::kUartChannel>(&output); 375 376 // Creating a channel with a dynamic ID (note that no output is provided; it 377 // will be set when the channel is used. 378 pw::rpc::Channel dynamic_channel; 379 380Sometimes, the ID and output of a channel are not known at compile time as they 381depend on information stored on the physical device. To support this use case, a 382dynamically-assignable channel can be configured once at runtime with an ID and 383output. 384 385.. code-block:: cpp 386 387 // Create a dynamic channel without a compile-time ID or output. 388 pw::rpc::Channel dynamic_channel; 389 390 void Init() { 391 // Called during boot to pull the channel configuration from the system. 392 dynamic_channel.Configure(GetChannelId(), some_output); 393 } 394 395Adding and removing channels 396============================ 397New channels may be registered with the ``OpenChannel`` function. If dynamic 398allocation is enabled (:c:macro:`PW_RPC_DYNAMIC_ALLOCATION` is 1), any number of 399channels may be registered. If dynamic allocation is disabled, new channels may 400only be registered if there are availale channel slots in the span provided to 401the RPC endpoint at construction. 402 403A channel may be closed and unregistered with an endpoint by calling 404``ChannelClose`` on the endpoint with the corresponding channel ID. This 405will terminate any pending calls and call their ``on_error`` callback 406with the ``ABORTED`` status. 407 408.. code-block:: cpp 409 410 // When a channel is closed, any pending calls will receive 411 // on_error callbacks with ABORTED status. 412 client->CloseChannel(1); 413 414-------- 415Services 416-------- 417A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc`` 418uses these .proto definitions to generate code for a base service, from which 419user-defined RPCs are implemented. 420 421``pw_rpc`` supports multiple protobuf libraries, and the generated code API 422depends on which is used. 423 424Services must be registered with a server in order to call their methods. 425Services may later be unregistered, which aborts calls for methods in that 426service and prevents future calls to them, until the service is re-registered. 427 428.. _module-pw_rpc-protobuf-library-apis: 429 430--------------------- 431Protobuf library APIs 432--------------------- 433 434.. toctree:: 435 :maxdepth: 1 436 437 pwpb/docs 438 nanopb/docs 439 440---------------------------- 441Testing a pw_rpc integration 442---------------------------- 443After setting up a ``pw_rpc`` server in your project, you can test that it is 444working as intended by registering the provided ``EchoService``, defined in 445``echo.proto``, which echoes back a message that it receives. 446 447.. literalinclude:: echo.proto 448 :language: protobuf 449 :lines: 14- 450 451For example, in C++ with pw_protobuf: 452 453.. code:: c++ 454 455 #include "pw_rpc/server.h" 456 457 // Include the apporpriate header for your protobuf library. 458 #include "pw_rpc/echo_service_pwpb.h" 459 460 constexpr pw::rpc::Channel kChannels[] = { /* ... */ }; 461 static pw::rpc::Server server(kChannels); 462 463 static pw::rpc::EchoService echo_service; 464 465 void Init() { 466 server.RegisterService(echo_service); 467 } 468 469Benchmarking and stress testing 470=============================== 471 472.. toctree:: 473 :maxdepth: 1 474 :hidden: 475 476 benchmark 477 478``pw_rpc`` provides an RPC service and Python module for stress testing and 479benchmarking a ``pw_rpc`` deployment. See :ref:`module-pw_rpc-benchmark`. 480 481------ 482Naming 483------ 484 485Reserved names 486============== 487``pw_rpc`` reserves a few service method names so they can be used for generated 488classes. The following names cannnot be used for service methods: 489 490- ``Client`` 491- ``Service`` 492- Any reserved words in the languages ``pw_rpc`` supports (e.g. ``class``). 493 494``pw_rpc`` does not reserve any service names, but the restriction of avoiding 495reserved words in supported languages applies. 496 497Service naming style 498==================== 499``pw_rpc`` service names should use capitalized camel case and should not use 500the term "Service". Appending "Service" to a service name is redundant, similar 501to appending "Class" or "Function" to a class or function name. The 502C++ implementation class may use "Service" in its name, however. 503 504For example, a service for accessing a file system should simply be named 505``service FileSystem``, rather than ``service FileSystemService``, in the 506``.proto`` file. 507 508.. code-block:: protobuf 509 510 // file.proto 511 package pw.file; 512 513 service FileSystem { 514 rpc List(ListRequest) returns (stream ListResponse); 515 } 516 517The C++ service implementation class may append "Service" to the name. 518 519.. code-block:: cpp 520 521 // file_system_service.h 522 #include "pw_file/file.raw_rpc.pb.h" 523 524 namespace pw::file { 525 526 class FileSystemService : public pw_rpc::raw::FileSystem::Service<FileSystemService> { 527 void List(ConstByteSpan request, RawServerWriter& writer); 528 }; 529 530 } // namespace pw::file 531 532For upstream Pigweed services, this naming style is a requirement. Note that 533some services created before this was established may use non-compliant 534names. For Pigweed users, this naming style is a suggestion. 535 536------------------------------ 537C++ payload sizing limitations 538------------------------------ 539The individual size of each sent RPC request or response is limited by 540``pw_rpc``'s ``PW_RPC_ENCODING_BUFFER_SIZE_BYTES`` configuration option when 541using Pigweed's C++ implementation. While multiple RPC messages can be enqueued 542(as permitted by the underlying transport), if a single individual sent message 543exceeds the limitations of the statically allocated encode buffer, the packet 544will fail to encode and be dropped. 545 546This applies to all C++ RPC service implementations (nanopb, raw, and pwpb), 547so it's important to ensure request and response message sizes do not exceed 548this limitation. 549 550As ``pw_rpc`` has some additional encoding overhead, a helper, 551``pw::rpc::MaxSafePayloadSize()`` is provided to expose the practical max RPC 552message payload size. 553 554.. code-block:: cpp 555 556 #include "pw_file/file.raw_rpc.pb.h" 557 #include "pw_rpc/channel.h" 558 559 namespace pw::file { 560 561 class FileSystemService : public pw_rpc::raw::FileSystem::Service<FileSystemService> { 562 public: 563 void List(ConstByteSpan request, RawServerWriter& writer); 564 565 private: 566 // Allocate a buffer for building proto responses. 567 static constexpr size_t kEncodeBufferSize = pw::rpc::MaxSafePayloadSize(); 568 std::array<std::byte, kEncodeBufferSize> encode_buffer_; 569 }; 570 571 } // namespace pw::file 572 573-------------------- 574Protocol description 575-------------------- 576Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These 577packets are used to send requests and responses, control streams, cancel ongoing 578RPCs, and report errors. 579 580Packet format 581============= 582Pigweed RPC packets consist of a type and a set of fields. The packets are 583encoded as protocol buffers. The full packet format is described in 584``pw_rpc/pw_rpc/internal/packet.proto``. 585 586.. literalinclude:: internal/packet.proto 587 :language: protobuf 588 :lines: 14- 589 590The packet type and RPC type determine which fields are present in a Pigweed RPC 591packet. Each packet type is only sent by either the client or the server. 592These tables describe the meaning of and fields included with each packet type. 593 594Client-to-server packets 595------------------------ 596+-------------------+-------------------------------------+ 597| packet type | description | 598+===================+=====================================+ 599| REQUEST | Invoke an RPC | 600| | | 601| | .. code-block:: text | 602| | | 603| | - channel_id | 604| | - service_id | 605| | - method_id | 606| | - payload | 607| | (unary & server streaming only) | 608| | - call_id (optional) | 609| | | 610+-------------------+-------------------------------------+ 611| CLIENT_STREAM | Message in a client stream | 612| | | 613| | .. code-block:: text | 614| | | 615| | - channel_id | 616| | - service_id | 617| | - method_id | 618| | - payload | 619| | - call_id (if set in REQUEST) | 620| | | 621+-------------------+-------------------------------------+ 622| CLIENT_STREAM_END | Client stream is complete | 623| | | 624| | .. code-block:: text | 625| | | 626| | - channel_id | 627| | - service_id | 628| | - method_id | 629| | - call_id (if set in REQUEST) | 630| | | 631+-------------------+-------------------------------------+ 632| CLIENT_ERROR | Abort an ongoing RPC | 633| | | 634| | .. code-block:: text | 635| | | 636| | - channel_id | 637| | - service_id | 638| | - method_id | 639| | - status | 640| | - call_id (if set in REQUEST) | 641| | | 642+-------------------+-------------------------------------+ 643 644**Client errors** 645 646The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet 647it did not request. If possible, the server should abort it. 648 649The status code indicates the type of error. The status code is logged, but all 650status codes result in the same action by the server: aborting the RPC. 651 652* ``CANCELLED`` -- The client requested that the RPC be cancelled. 653* ``ABORTED`` -- The RPC was aborted due its channel being closed. 654* ``NOT_FOUND`` -- Received a packet for a service method the client does not 655 recognize. 656* ``FAILED_PRECONDITION`` -- Received a packet for a service method that the 657 client did not invoke. 658* ``DATA_LOSS`` -- Received a corrupt packet for a pending service method. 659* ``INVALID_ARGUMENT`` -- The server sent a packet type to an RPC that does not 660 support it (a ``SERVER_STREAM`` was sent to an RPC with no server stream). 661* ``UNAVAILABLE`` -- Received a packet for an unknown channel. 662 663Server-to-client packets 664------------------------ 665+-------------------+-------------------------------------+ 666| packet type | description | 667+===================+=====================================+ 668| RESPONSE | The RPC is complete | 669| | | 670| | .. code-block:: text | 671| | | 672| | - channel_id | 673| | - service_id | 674| | - method_id | 675| | - status | 676| | - payload | 677| | (unary & client streaming only) | 678| | - call_id (if set in REQUEST) | 679| | | 680+-------------------+-------------------------------------+ 681| SERVER_STREAM | Message in a server stream | 682| | | 683| | .. code-block:: text | 684| | | 685| | - channel_id | 686| | - service_id | 687| | - method_id | 688| | - payload | 689| | - call_id (if set in REQUEST) | 690| | | 691+-------------------+-------------------------------------+ 692| SERVER_ERROR | Received unexpected packet | 693| | | 694| | .. code-block:: text | 695| | | 696| | - channel_id | 697| | - service_id (if relevant) | 698| | - method_id (if relevant) | 699| | - status | 700| | - call_id (if set in REQUEST) | 701| | | 702+-------------------+-------------------------------------+ 703 704All server packets contain the same ``call_id`` that was set in the initial 705request made by the client, if any. 706 707**Server errors** 708 709The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot 710process. The client should abort any RPC for which it receives an error. The 711status field indicates the type of error. 712 713* ``NOT_FOUND`` -- The requested service or method does not exist. 714* ``FAILED_PRECONDITION`` -- A client stream or cancel packet was sent for an 715 RPC that is not pending. 716* ``INVALID_ARGUMENT`` -- The client sent a packet type to an RPC that does not 717 support it (a ``CLIENT_STREAM`` was sent to an RPC with no client stream). 718* ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel 719 could not be allocated for it. 720* ``ABORTED`` -- The RPC was aborted due its channel being closed. 721* ``INTERNAL`` -- The server was unable to respond to an RPC due to an 722 unrecoverable internal error. 723* ``UNAVAILABLE`` -- Received a packet for an unknown channel. 724 725Inovking a service method 726========================= 727Calling an RPC requires a specific sequence of packets. This section describes 728the protocol for calling service methods of each type: unary, server streaming, 729client streaming, and bidirectional streaming. 730 731The basic flow for all RPC invocations is as follows: 732 733 * Client sends a ``REQUEST`` packet. Includes a payload for unary & server 734 streaming RPCs. 735 * For client and bidirectional streaming RPCs, the client may send any number 736 of ``CLIENT_STREAM`` packets with payloads. 737 * For server and bidirectional streaming RPCs, the server may send any number 738 of ``SERVER_STREAM`` packets. 739 * The server sends a ``RESPONSE`` packet. Includes a payload for unary & 740 client streaming RPCs. The RPC is complete. 741 742The client may cancel an ongoing RPC at any time by sending a ``CLIENT_ERROR`` 743packet with status ``CANCELLED``. The server may finish an ongoing RPC at any 744time by sending the ``RESPONSE`` packet. 745 746Unary RPC 747--------- 748In a unary RPC, the client sends a single request and the server sends a single 749response. 750 751.. image:: unary_rpc.svg 752 753The client may attempt to cancel a unary RPC by sending a ``CLIENT_ERROR`` 754packet with status ``CANCELLED``. The server sends no response to a cancelled 755RPC. If the server processes the unary RPC synchronously (the handling thread 756sends the response), it may not be possible to cancel the RPC. 757 758.. image:: unary_rpc_cancelled.svg 759 760Server streaming RPC 761-------------------- 762In a server streaming RPC, the client sends a single request and the server 763sends any number of ``SERVER_STREAM`` packets followed by a ``RESPONSE`` packet. 764 765.. image:: server_streaming_rpc.svg 766 767The client may terminate a server streaming RPC by sending a ``CLIENT_STREAM`` 768packet with status ``CANCELLED``. The server sends no response. 769 770.. image:: server_streaming_rpc_cancelled.svg 771 772Client streaming RPC 773-------------------- 774In a client streaming RPC, the client starts the RPC by sending a ``REQUEST`` 775packet with no payload. It then sends any number of messages in 776``CLIENT_STREAM`` packets, followed by a ``CLIENT_STREAM_END``. The server sends 777a single ``RESPONSE`` to finish the RPC. 778 779.. image:: client_streaming_rpc.svg 780 781The server may finish the RPC at any time by sending its ``RESPONSE`` packet, 782even if it has not yet received the ``CLIENT_STREAM_END`` packet. The client may 783terminate the RPC at any time by sending a ``CLIENT_ERROR`` packet with status 784``CANCELLED``. 785 786.. image:: client_streaming_rpc_cancelled.svg 787 788Bidirectional streaming RPC 789--------------------^^^^^^^ 790In a bidirectional streaming RPC, the client sends any number of requests and 791the server sends any number of responses. The client invokes the RPC by sending 792a ``REQUEST`` with no payload. It sends a ``CLIENT_STREAM_END`` packet when it 793has finished sending requests. The server sends a ``RESPONSE`` packet to finish 794the RPC. 795 796.. image:: bidirectional_streaming_rpc.svg 797 798The server may finish the RPC at any time by sending the ``RESPONSE`` packet, 799even if it has not received the ``CLIENT_STREAM_END`` packet. The client may 800terminate the RPC at any time by sending a ``CLIENT_ERROR`` packet with status 801``CANCELLED``. 802 803.. image:: bidirectional_streaming_rpc_cancelled.svg 804 805------- 806C++ API 807------- 808 809RPC server 810========== 811Declare an instance of ``rpc::Server`` and register services with it. 812 813.. admonition:: TODO 814 815 Document the public interface 816 817Size report 818----------- 819The following size report showcases the memory usage of the core RPC server. It 820is configured with a single channel using a basic transport interface that 821directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte 822packet buffer, which comprises the plurality of the example's RAM usage. This is 823not a suitable transport for an actual product; a real implementation would have 824additional overhead proportional to the complexity of the transport. 825 826.. include:: server_size 827 828RPC server implementation 829------------------------- 830 831The Method class 832^^^^^^^^^^^^^^^^ 833The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method`` 834serves as the bridge between the ``pw_rpc`` server library and the user-defined 835RPC functions. Each supported protobuf implementation extends ``Method`` to 836implement its request and response proto handling. The ``pw_rpc`` server 837calls into the ``Method`` implementation through the base class's ``Invoke`` 838function. 839 840``Method`` implementations store metadata about each method, including a 841function pointer to the user-defined method implementation. They also provide 842``static constexpr`` functions for creating each type of method. ``Method`` 843implementations must satisfy the ``MethodImplTester`` test class in 844``pw_rpc/internal/method_impl_tester.h``. 845 846See ``pw_rpc/internal/method.h`` for more details about ``Method``. 847 848Packet flow 849^^^^^^^^^^^ 850 851Requests 852........ 853 854.. image:: request_packets.svg 855 856Responses 857......... 858 859.. image:: response_packets.svg 860 861RPC client 862========== 863The RPC client is used to send requests to a server and manages the contexts of 864ongoing RPCs. 865 866Setting up a client 867------------------- 868The ``pw::rpc::Client`` class is instantiated with a list of channels that it 869uses to communicate. These channels can be shared with a server, but multiple 870clients cannot use the same channels. 871 872To send incoming RPC packets from the transport layer to be processed by a 873client, the client's ``ProcessPacket`` function is called with the packet data. 874 875.. code:: c++ 876 877 #include "pw_rpc/client.h" 878 879 namespace { 880 881 pw::rpc::Channel my_channels[] = { 882 pw::rpc::Channel::Create<1>(&my_channel_output)}; 883 pw::rpc::Client my_client(my_channels); 884 885 } // namespace 886 887 // Called when the transport layer receives an RPC packet. 888 void ProcessRpcPacket(ConstByteSpan packet) { 889 my_client.ProcessPacket(packet); 890 } 891 892Note that client processing such as callbacks will be invoked within 893the body of ``ProcessPacket``. 894 895If certain packets need to be filtered out, or if certain client processing 896needs to be invoked from a specific thread or context, the ``PacketMeta`` class 897can be used to determine which service or channel a packet is targeting. After 898filtering, ``ProcessPacket`` can be called from the appropriate environment. 899 900.. _module-pw_rpc-making-calls: 901 902Making RPC calls 903---------------- 904RPC calls are not made directly through the client, but using one of its 905registered channels instead. A service client class is generated from a .proto 906file for each selected protobuf library, which is then used to send RPC requests 907through a given channel. The API for this depends on the protobuf library; 908please refer to the 909:ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple 910service client implementations can exist simulatenously and share the same 911``Client`` class. 912 913When a call is made, a call object is returned to the caller. This object tracks 914the ongoing RPC call, and can be used to manage it. An RPC call is only active 915as long as its call object is alive. 916 917.. tip:: 918 Use ``std::move`` when passing around call objects to keep RPCs alive. 919 920Example 921^^^^^^^ 922.. code-block:: c++ 923 924 #include "pw_rpc/echo_service_nanopb.h" 925 926 namespace { 927 // Generated clients are namespaced with their proto library. 928 using EchoClient = pw_rpc::nanopb::EchoService::Client; 929 930 // RPC channel ID on which to make client calls. RPC calls cannot be made on 931 // channel 0 (Channel::kUnassignedChannelId). 932 constexpr uint32_t kDefaultChannelId = 1; 933 934 pw::rpc::NanopbUnaryReceiver<pw_rpc_EchoMessage> echo_call; 935 936 // Callback invoked when a response is received. This is called synchronously 937 // from Client::ProcessPacket. 938 void EchoResponse(const pw_rpc_EchoMessage& response, 939 pw::Status status) { 940 if (status.ok()) { 941 PW_LOG_INFO("Received echo response: %s", response.msg); 942 } else { 943 PW_LOG_ERROR("Echo failed with status %d", 944 static_cast<int>(status.code())); 945 } 946 } 947 948 } // namespace 949 950 void CallEcho(const char* message) { 951 // Create a client to call the EchoService. 952 EchoClient echo_client(my_rpc_client, kDefaultChannelId); 953 954 pw_rpc_EchoMessage request{}; 955 pw::string::Copy(message, request.msg); 956 957 // By assigning the returned call to the global echo_call, the RPC 958 // call is kept alive until it completes. When a response is received, it 959 // will be logged by the handler function and the call will complete. 960 echo_call = echo_client.Echo(request, EchoResponse); 961 if (!echo_call.active()) { 962 // The RPC call was not sent. This could occur due to, for example, an 963 // invalid channel ID. Handle if necessary. 964 } 965 } 966 967Call objects 968============ 969An RPC call is represented by a call object. Server and client calls use the 970same base call class in C++, but the public API is different depending on the 971type of call (see `RPC call lifecycle`_) and whether it is being used by the 972server or client. 973 974The public call types are as follows: 975 976.. list-table:: 977 :header-rows: 1 978 979 * - RPC Type 980 - Server call 981 - Client call 982 * - Unary 983 - ``(Raw|Nanopb|Pwpb)UnaryResponder`` 984 - ``(Raw|Nanopb|Pwpb)UnaryReceiver`` 985 * - Server streaming 986 - ``(Raw|Nanopb|Pwpb)ServerWriter`` 987 - ``(Raw|Nanopb|Pwpb)ClientReader`` 988 * - Client streaming 989 - ``(Raw|Nanopb|Pwpb)ServerReader`` 990 - ``(Raw|Nanopb|Pwpb)ClientWriter`` 991 * - Bidirectional streaming 992 - ``(Raw|Nanopb|Pwpb)ServerReaderWriter`` 993 - ``(Raw|Nanopb|Pwpb)ClientReaderWriter`` 994 995Client call API 996--------------- 997Client call objects provide a few common methods. 998 999.. cpp:class:: pw::rpc::ClientCallType 1000 1001 The ``ClientCallType`` will be one of the following types: 1002 1003 - ``(Raw|Nanopb|Pwpb)UnaryReceiver`` for unary 1004 - ``(Raw|Nanopb|Pwpb)ClientReader`` for server streaming 1005 - ``(Raw|Nanopb|Pwpb)ClientWriter`` for client streaming 1006 - ``(Raw|Nanopb|Pwpb)ClientReaderWriter`` for bidirectional streaming 1007 1008 .. cpp:function:: bool active() const 1009 1010 Returns true if the call is active. 1011 1012 .. cpp:function:: uint32_t channel_id() const 1013 1014 Returns the channel ID of this call, which is 0 if the call is inactive. 1015 1016 .. cpp:function:: uint32_t id() const 1017 1018 Returns the call ID, a unique identifier for this call. 1019 1020 .. cpp:function:: void Write(RequestType) 1021 1022 Only available on client and bidirectional streaming calls. Sends a stream 1023 request. Returns: 1024 1025 - ``OK`` - the request was successfully sent 1026 - ``FAILED_PRECONDITION`` - the writer is closed 1027 - ``INTERNAL`` - pw_rpc was unable to encode message; does not apply to raw 1028 calls 1029 - other errors - the :cpp:class:`ChannelOutput` failed to send the packet; 1030 the error codes are determined by the :cpp:class:`ChannelOutput` 1031 implementation 1032 1033 .. cpp:function:: pw::Status CloseClientStream() 1034 1035 Only available on client and bidirectional streaming calls. Notifies the 1036 server that no further client stream messages will be sent. 1037 1038 .. cpp:function:: pw::Status Cancel() 1039 1040 Cancels this RPC. Closes the call and sends a ``CANCELLED`` error to the 1041 server. Return statuses are the same as :cpp:func:`Write`. 1042 1043 .. cpp:function:: void Abandon() 1044 1045 Closes this RPC locally. Sends a ``CLIENT_STREAM_END``, but no cancellation 1046 packet. Future packets for this RPC are dropped, and the client sends a 1047 ``FAILED_PRECONDITION`` error in response because the call is not active. 1048 1049 .. cpp:function:: void set_on_completed(pw::Function<void(ResponseTypeIfUnaryOnly, pw::Status)>) 1050 1051 Sets the callback that is called when the RPC completes normally. The 1052 signature depends on whether the call has a unary or stream response. 1053 1054 .. cpp:function:: void set_on_error(pw::Function<void(pw::Status)>) 1055 1056 Sets the callback that is called when the RPC is terminated due to an error. 1057 1058 .. cpp:function:: void set_on_next(pw::Function<void(ResponseType)>) 1059 1060 Only available on server and bidirectional streaming calls. Sets the callback 1061 that is called for each stream response. 1062 1063Callbacks 1064--------- 1065The C++ call objects allow users to set callbacks that are invoked when RPC 1066`events`_ occur. 1067 1068.. list-table:: 1069 :header-rows: 1 1070 1071 * - Name 1072 - Stream signature 1073 - Non-stream signature 1074 - Server 1075 - Client 1076 * - ``on_error`` 1077 - ``void(pw::Status)`` 1078 - ``void(pw::Status)`` 1079 - ✅ 1080 - ✅ 1081 * - ``on_next`` 1082 - n/a 1083 - ``void(const PayloadType&)`` 1084 - ✅ 1085 - ✅ 1086 * - ``on_completed`` 1087 - ``void(pw::Status)`` 1088 - ``void(const PayloadType&, pw::Status)`` 1089 - 1090 - ✅ 1091 * - ``on_client_stream_end`` 1092 - ``void()`` 1093 - n/a 1094 - ✅ (:c:macro:`optional <PW_RPC_CLIENT_STREAM_END_CALLBACK>`) 1095 - 1096 1097Limitations and restrictions 1098^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1099RPC callbacks are free to perform most actions, including invoking new RPCs or 1100cancelling pending calls. However, the C++ implementation imposes some 1101limitations and restrictions that must be observed. 1102 1103Destructors & moves wait for callbacks to complete 1104................................................... 1105* Callbacks must not destroy their call object. Attempting to do so will result 1106 in deadlock. 1107* Other threads may destroy a call while its callback is running, but that 1108 thread will block until all callbacks complete. 1109* Callbacks must not move their call object if it the call is still active. They 1110 may move their call object after it has terminated. Callbacks may move a 1111 different call into their call object, since moving closes the destination 1112 call. 1113* Other threads may move a call object while it has a callback running, but they 1114 will block until the callback completes if the call is still active. 1115 1116.. warning:: 1117 1118 Deadlocks or crashes occur if a callback: 1119 1120 - attempts to destroy its call object 1121 - attempts to move its call object while the call is still active 1122 - never returns 1123 1124 If ``pw_rpc`` a callback violates these restrictions, a crash may occur, 1125 depending on the value of :c:macro:`PW_RPC_CALLBACK_TIMEOUT_TICKS`. These 1126 crashes have a message like the following: 1127 1128 .. code-block:: text 1129 1130 A callback for RPC 1:cc0f6de0/31e616ce has not finished after 10000 ticks. 1131 This may indicate that an RPC callback attempted to destroy or move its own 1132 call object, which is not permitted. Fix this condition or change the value of 1133 PW_RPC_CALLBACK_TIMEOUT_TICKS to avoid this crash. 1134 1135 See https://pigweed.dev/pw_rpc#destructors-moves-wait-for-callbacks-to-complete 1136 for details. 1137 1138Only one thread at a time may execute ``on_next`` 1139................................................. 1140Only one thread may execute the ``on_next`` callback for a specific service 1141method at a time. If a second thread calls ``ProcessPacket()`` with a stream 1142packet before the ``on_next`` callback for the previous packet completes, the 1143second packet will be dropped. The RPC endpoint logs a warning when this occurs. 1144 1145Example warning for a dropped stream packet: 1146 1147.. code-block:: text 1148 1149 WRN Received stream packet for 1:cc0f6de0/31e616ce before the callback for 1150 a previous packet completed! This packet will be dropped. This can be 1151 avoided by handling packets for a particular RPC on only one thread. 1152 1153RPC calls introspection 1154======================= 1155``pw_rpc`` provides ``pw_rpc/method_info.h`` header that allows to obtain 1156information about the generated RPC method in compile time. 1157 1158For now it provides only two types: ``MethodRequestType<RpcMethod>`` and 1159``MethodResponseType<RpcMethod>``. They are aliases to the types that are used 1160as a request and response respectively for the given RpcMethod. 1161 1162Example 1163------- 1164We have an RPC service ``SpecialService`` with ``MyMethod`` method: 1165 1166.. code-block:: protobuf 1167 1168 package some.package; 1169 service SpecialService { 1170 rpc MyMethod(MyMethodRequest) returns (MyMethodResponse) {} 1171 } 1172 1173We also have a templated Storage type alias: 1174 1175.. code-block:: c++ 1176 1177 template <auto kMethod> 1178 using Storage = 1179 std::pair<MethodRequestType<kMethod>, MethodResponseType<kMethod>>; 1180 1181``Storage<some::package::pw_rpc::pwpb::SpecialService::MyMethod>`` will 1182instantiate as: 1183 1184.. code-block:: c++ 1185 1186 std::pair<some::package::MyMethodRequest::Message, 1187 some::package::MyMethodResponse::Message>; 1188 1189.. note:: 1190 1191 Only nanopb and pw_protobuf have real types as 1192 ``MethodRequestType<RpcMethod>``/``MethodResponseType<RpcMethod>``. Raw has 1193 them both set as ``void``. In reality, they are ``pw::ConstByteSpan``. Any 1194 helper/trait that wants to use this types for raw methods should do a custom 1195 implementation that copies the bytes under the span instead of copying just 1196 the span. 1197 1198Client Synchronous Call wrappers 1199================================ 1200If synchronous behavior is desired when making client calls, users can use one 1201of the ``SynchronousCall<RpcMethod>`` wrapper functions to make their RPC call. 1202These wrappers effectively wrap the asynchronous Client RPC call with a timed 1203thread notification and return once a result is known or a timeout has occurred. 1204These return a ``SynchronousCallResult<Response>`` object, which can be queried 1205to determine whether any error scenarios occurred and, if not, access the 1206response. 1207 1208``SynchronousCall<RpcMethod>`` will block indefinitely, whereas 1209``SynchronousCallFor<RpcMethod>`` and ``SynchronousCallUntil<RpcMethod>`` will 1210block for a given timeout or until a deadline, respectively. All wrappers work 1211with both the standalone static RPC functions and the generated Client member 1212methods. 1213 1214.. note:: Use of the SynchronousCall wrappers requires a TimedThreadNotification 1215 backend. 1216.. note:: Only nanopb and pw_protobuf Unary RPC methods are supported. 1217 1218Example 1219------- 1220.. code-block:: c++ 1221 1222 #include "pw_rpc/synchronous_call.h" 1223 1224 void InvokeUnaryRpc() { 1225 pw::rpc::Client client; 1226 pw::rpc::Channel channel; 1227 1228 RoomInfoRequest request; 1229 SynchronousCallResult<RoomInfoResponse> result = 1230 SynchronousCall<Chat::GetRoomInformation>(client, channel.id(), request); 1231 1232 if (result.is_rpc_error()) { 1233 ShutdownClient(client); 1234 } else if (result.is_server_error()) { 1235 HandleServerError(result.status()); 1236 } else if (result.is_timeout()) { 1237 // SynchronousCall will block indefinitely, so we should never get here. 1238 PW_UNREACHABLE(); 1239 } 1240 HandleRoomInformation(std::move(result).response()); 1241 } 1242 1243 void AnotherExample() { 1244 pw_rpc::nanopb::Chat::Client chat_client(client, channel); 1245 constexpr auto kTimeout = pw::chrono::SystemClock::for_at_least(500ms); 1246 1247 RoomInfoRequest request; 1248 auto result = SynchronousCallFor<Chat::GetRoomInformation>( 1249 chat_client, request, kTimeout); 1250 1251 if (result.is_timeout()) { 1252 RetryRoomRequest(); 1253 } else { 1254 ... 1255 } 1256 } 1257 1258The ``SynchronousCallResult<Response>`` is also compatible with the PW_TRY 1259family of macros, but users should be aware that their use will lose information 1260about the type of error. This should only be used if the caller will handle all 1261error scenarios the same. 1262 1263.. code-block:: c++ 1264 1265 pw::Status SyncRpc() { 1266 const RoomInfoRequest request; 1267 PW_TRY_ASSIGN(const RoomInfoResponse& response, 1268 SynchronousCall<Chat::GetRoomInformation>(client, request)); 1269 HandleRoomInformation(response); 1270 return pw::OkStatus(); 1271 } 1272 1273ClientServer 1274============ 1275Sometimes, a device needs to both process RPCs as a server, as well as making 1276calls to another device as a client. To do this, both a client and server must 1277be set up, and incoming packets must be sent to both of them. 1278 1279Pigweed simplifies this setup by providing a ``ClientServer`` class which wraps 1280an RPC client and server with the same set of channels. 1281 1282.. code-block:: cpp 1283 1284 pw::rpc::Channel channels[] = { 1285 pw::rpc::Channel::Create<1>(&channel_output)}; 1286 1287 // Creates both a client and a server. 1288 pw::rpc::ClientServer client_server(channels); 1289 1290 void ProcessRpcData(pw::ConstByteSpan packet) { 1291 // Calls into both the client and the server, sending the packet to the 1292 // appropriate one. 1293 client_server.ProcessPacket(packet); 1294 } 1295 1296Testing 1297======= 1298``pw_rpc`` provides utilities for unit testing RPC services and client calls. 1299 1300Client unit testing in C++ 1301-------------------------- 1302``pw_rpc`` supports invoking RPCs, simulating server responses, and checking 1303what packets are sent by an RPC client in tests. Raw, Nanopb and Pwpb interfaces 1304are supported. Code that uses the raw API may be tested with the raw test 1305helpers, and vice versa. The Nanopb and Pwpb APIs also provides a test helper 1306with a real client-server pair that supports testing of asynchronous messaging. 1307 1308To test sychronous code that invokes RPCs, declare a ``RawClientTestContext``, 1309``PwpbClientTestContext``, or ``NanopbClientTestContext``. These test context 1310objects provide a preconfigured RPC client, channel, server fake, and buffer for 1311encoding packets. 1312 1313These test classes are defined in ``pw_rpc/raw/client_testing.h``, 1314``pw_rpc/pwpb/client_testing.h``, or ``pw_rpc/nanopb/client_testing.h``. 1315 1316Use the context's ``client()`` and ``channel()`` to invoke RPCs. Use the 1317context's ``server()`` to simulate responses. To verify that the client sent the 1318expected data, use the context's ``output()``, which is a ``FakeChannelOutput``. 1319 1320For example, the following tests a class that invokes an RPC. It checks that 1321the expected data was sent and then simulates a response from the server. 1322 1323.. code-block:: cpp 1324 1325 #include "pw_rpc/raw/client_testing.h" 1326 1327 class ClientUnderTest { 1328 public: 1329 // To support injecting an RPC client for testing, classes that make RPC 1330 // calls should take an RPC client and channel ID or an RPC service client 1331 // (e.g. pw_rpc::raw::MyService::Client). 1332 ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id); 1333 1334 void DoSomethingThatInvokesAnRpc(); 1335 1336 bool SetToTrueWhenRpcCompletes(); 1337 }; 1338 1339 TEST(TestAThing, InvokesRpcAndHandlesResponse) { 1340 RawClientTestContext context; 1341 ClientUnderTest thing(context.client(), context.channel().id()); 1342 1343 // Execute the code that invokes the MyService.TheMethod RPC. 1344 things.DoSomethingThatInvokesAnRpc(); 1345 1346 // Find and verify the payloads sent for the MyService.TheMethod RPC. 1347 auto msgs = context.output().payloads<pw_rpc::raw::MyService::TheMethod>(); 1348 ASSERT_EQ(msgs.size(), 1u); 1349 1350 VerifyThatTheExpectedMessageWasSent(msgs.back()); 1351 1352 // Send the response packet from the server and verify that the class reacts 1353 // accordingly. 1354 EXPECT_FALSE(thing.SetToTrueWhenRpcCompletes()); 1355 1356 context_.server().SendResponse<pw_rpc::raw::MyService::TheMethod>( 1357 final_message, OkStatus()); 1358 1359 EXPECT_TRUE(thing.SetToTrueWhenRpcCompletes()); 1360 } 1361 1362To test client code that uses asynchronous responses, encapsulates multiple 1363rpc calls to one or more services, or uses a custom service implemenation, 1364declare a ``NanopbClientServerTestContextThreaded`` or 1365``PwpbClientServerTestContextThreaded``. These test object are defined in 1366``pw_rpc/nanopb/client_server_testing_threaded.h`` and 1367``pw_rpc/pwpb/client_server_testing_threaded.h``. 1368 1369Use the context's ``server()`` to register a ``Service`` implementation, and 1370``client()`` and ``channel()`` to invoke RPCs. Create a ``Thread`` using the 1371context as a ``ThreadCore`` to have it asycronously forward request/responses or 1372call ``ForwardNewPackets`` to synchronously process all messages. To verify that 1373the client/server sent the expected data, use the context's 1374``request(uint32_t index)`` and ``response(uint32_t index)`` to retrieve the 1375ordered messages. 1376 1377For example, the following tests a class that invokes an RPC and blocks till a 1378response is received. It verifies that expected data was both sent and received. 1379 1380.. code-block:: cpp 1381 1382 #include "my_library_protos/my_service.rpc.pb.h" 1383 #include "pw_rpc/nanopb/client_server_testing_threaded.h" 1384 #include "pw_thread_stl/options.h" 1385 1386 class ClientUnderTest { 1387 public: 1388 // To support injecting an RPC client for testing, classes that make RPC 1389 // calls should take an RPC client and channel ID or an RPC service client 1390 // (e.g. pw_rpc::raw::MyService::Client). 1391 ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id); 1392 1393 Status BlockOnResponse(uint32_t value); 1394 }; 1395 1396 1397 class TestService final : public MyService<TestService> { 1398 public: 1399 Status TheMethod(const pw_rpc_test_TheMethod& request, 1400 pw_rpc_test_TheMethod& response) { 1401 response.value = request.integer + 1; 1402 return pw::OkStatus(); 1403 } 1404 }; 1405 1406 TEST(TestServiceTest, ReceivesUnaryRpcReponse) { 1407 NanopbClientServerTestContextThreaded<> ctx(pw::thread::stl::Options{}); 1408 TestService service; 1409 ctx.server().RegisterService(service); 1410 ClientUnderTest client(ctx.client(), ctx.channel().id()); 1411 1412 // Execute the code that invokes the MyService.TheMethod RPC. 1413 constexpr uint32_t value = 1; 1414 const auto result = client.BlockOnResponse(value); 1415 const auto request = ctx.request<MyService::TheMethod>(0); 1416 const auto response = ctx.resonse<MyService::TheMethod>(0); 1417 1418 // Verify content of messages 1419 EXPECT_EQ(result, pw::OkStatus()); 1420 EXPECT_EQ(request.value, value); 1421 EXPECT_EQ(response.value, value + 1); 1422 } 1423 1424Synchronous versions of these test contexts also exist that may be used on 1425non-threaded systems ``NanopbClientServerTestContext`` and 1426``PwpbClientServerTestContext``. While these do not allow for asynchronous 1427messaging they support the use of service implemenations and use a similar 1428syntax. When these are used ``.ForwardNewPackets()`` should be called after each 1429rpc call to trigger sending of queued messages. 1430 1431For example, the following tests a class that invokes an RPC that is responded 1432to with a test service implemenation. 1433 1434.. code-block:: cpp 1435 1436 #include "my_library_protos/my_service.rpc.pb.h" 1437 #include "pw_rpc/nanopb/client_server_testing.h" 1438 1439 class ClientUnderTest { 1440 public: 1441 ClientUnderTest(pw::rpc::Client& client, uint32_t channel_id); 1442 1443 Status SendRpcCall(uint32_t value); 1444 }; 1445 1446 1447 class TestService final : public MyService<TestService> { 1448 public: 1449 Status TheMethod(const pw_rpc_test_TheMethod& request, 1450 pw_rpc_test_TheMethod& response) { 1451 response.value = request.integer + 1; 1452 return pw::OkStatus(); 1453 } 1454 }; 1455 1456 TEST(TestServiceTest, ReceivesUnaryRpcReponse) { 1457 NanopbClientServerTestContext<> ctx(); 1458 TestService service; 1459 ctx.server().RegisterService(service); 1460 ClientUnderTest client(ctx.client(), ctx.channel().id()); 1461 1462 // Execute the code that invokes the MyService.TheMethod RPC. 1463 constexpr uint32_t value = 1; 1464 const auto result = client.SendRpcCall(value); 1465 // Needed after ever RPC call to trigger forward of packets 1466 ctx.ForwardNewPackets(); 1467 const auto request = ctx.request<MyService::TheMethod>(0); 1468 const auto response = ctx.resonse<MyService::TheMethod>(0); 1469 1470 // Verify content of messages 1471 EXPECT_EQ(result, pw::OkStatus()); 1472 EXPECT_EQ(request.value, value); 1473 EXPECT_EQ(response.value, value + 1); 1474 } 1475 1476SendResponseIfCalled() helper 1477----------------------------- 1478``SendResponseIfCalled()`` function waits on ``*ClientTestContext*`` output to 1479have a call for the specified method and then responses to it. It supports 1480timeout for the waiting part (default timeout is 100ms). 1481 1482.. code:: c++ 1483 1484 #include "pw_rpc/test_helpers.h" 1485 1486 pw::rpc::PwpbClientTestContext client_context; 1487 other::pw_rpc::pwpb::OtherService::Client other_service_client( 1488 client_context.client(), client_context.channel().id()); 1489 1490 PW_PWPB_TEST_METHOD_CONTEXT(MyService, GetData) 1491 context(other_service_client); 1492 context.call({}); 1493 1494 ASSERT_OK(pw::rpc::test::SendResponseIfCalled< 1495 other::pw_rpc::pwpb::OtherService::GetPart>( 1496 client_context, {.value = 42})); 1497 1498 // At this point MyService::GetData handler received the GetPartResponse. 1499 1500Integration testing with ``pw_rpc`` 1501----------------------------------- 1502``pw_rpc`` provides utilities to simplify writing integration tests for systems 1503that communicate with ``pw_rpc``. The integration test utitilies set up a socket 1504to use for IPC between an RPC server and client process. 1505 1506The server binary uses the system RPC server facade defined 1507``pw_rpc_system_server/rpc_server.h``. The client binary uses the functions 1508defined in ``pw_rpc/integration_testing.h``: 1509 1510.. cpp:var:: constexpr uint32_t kChannelId 1511 1512 The RPC channel for integration test RPCs. 1513 1514.. cpp:function:: pw::rpc::Client& pw::rpc::integration_test::Client() 1515 1516 Returns the global RPC client for integration test use. 1517 1518.. cpp:function:: pw::Status pw::rpc::integration_test::InitializeClient(int argc, char* argv[], const char* usage_args = "PORT") 1519 1520 Initializes logging and the global RPC client for integration testing. Starts 1521 a background thread that processes incoming. 1522 1523Module Configuration Options 1524============================ 1525The following configurations can be adjusted via compile-time configuration of 1526this module, see the 1527:ref:`module documentation <module-structure-compile-time-configuration>` for 1528more details. 1529 1530.. doxygenfile:: pw_rpc/public/pw_rpc/internal/config.h 1531 :sections: define 1532 1533Sharing server and client code 1534============================== 1535Streaming RPCs support writing multiple requests or responses. To facilitate 1536sharing code between servers and clients, ``pw_rpc`` provides the 1537``pw::rpc::Writer`` interface. On the client side, a client or bidirectional 1538streaming RPC call object (``ClientWriter`` or ``ClientReaderWriter``) can be 1539used as a ``pw::rpc::Writer&``. On the server side, a server or bidirectional 1540streaming RPC call object (``ServerWriter`` or ``ServerReaderWriter``) can be 1541used as a ``pw::rpc::Writer&``. 1542 1543Zephyr 1544====== 1545To enable ``pw_rpc.*`` for Zephyr add ``CONFIG_PIGWEED_RPC=y`` to the project's 1546configuration. This will enable the Kconfig menu for the following: 1547 1548* ``pw_rpc.server`` which can be enabled via ``CONFIG_PIGWEED_RPC_SERVER=y``. 1549* ``pw_rpc.client`` which can be enabled via ``CONFIG_PIGWEED_RPC_CLIENT=y``. 1550* ``pw_rpc.client_server`` which can be enabled via 1551 ``CONFIG_PIGWEED_RPC_CLIENT_SERVER=y``. 1552* ``pw_rpc.common` which can be enabled via ``CONFIG_PIGWEED_RPC_COMMON=y``. 1553 1554Encoding and sending packets 1555============================ 1556``pw_rpc`` has to manage interactions among multiple RPC clients, servers, 1557client calls, and server calls. To safely synchronize these interactions with 1558minimal overhead, ``pw_rpc`` uses a single, global mutex (when 1559``PW_RPC_USE_GLOBAL_MUTEX`` is enabled). 1560 1561Because ``pw_rpc`` uses a global mutex, it also uses a global buffer to encode 1562outgoing packets. The size of the buffer is set with 1563``PW_RPC_ENCODING_BUFFER_SIZE_BYTES``, which defaults to 512 B. If dynamic 1564allocation is enabled, this size does not affect how large RPC messages can be, 1565but it is still used for sizing buffers in test utilities. 1566 1567Users of ``pw_rpc`` must implement the :cpp:class:`pw::rpc::ChannelOutput` 1568interface. 1569 1570.. _module-pw_rpc-ChannelOutput: 1571.. cpp:class:: pw::rpc::ChannelOutput 1572 1573 ``pw_rpc`` endpoints use :cpp:class:`ChannelOutput` instances to send packets. 1574 Systems that integrate pw_rpc must use one or more :cpp:class:`ChannelOutput` 1575 instances. 1576 1577 .. cpp:member:: static constexpr size_t kUnlimited = std::numeric_limits<size_t>::max() 1578 1579 Value returned from :cpp:func:`MaximumTransmissionUnit` to indicate an 1580 unlimited MTU. 1581 1582 .. cpp:function:: virtual size_t MaximumTransmissionUnit() 1583 1584 Returns the size of the largest packet the :cpp:class:`ChannelOutput` can 1585 send. :cpp:class:`ChannelOutput` implementations should only override this 1586 function if they impose a limit on the MTU. The default implementation 1587 returns :cpp:member:`kUnlimited`, which indicates that there is no MTU 1588 limit. 1589 1590 .. cpp:function:: virtual pw::Status Send(span<std::byte> packet) 1591 1592 Sends an encoded RPC packet. Returns OK if further packets may be sent, even 1593 if the current packet could not be sent. Returns any other status if the 1594 Channel is no longer able to send packets. 1595 1596 The RPC system's internal lock is held while this function is called. Avoid 1597 long-running operations, since these will delay any other users of the RPC 1598 system. 1599 1600 .. danger:: 1601 1602 No ``pw_rpc`` APIs may be accessed in this function! Implementations MUST 1603 NOT access any RPC endpoints (:cpp:class:`pw::rpc::Client`, 1604 :cpp:class:`pw::rpc::Server`) or call objects 1605 (:cpp:class:`pw::rpc::ServerReaderWriter`, 1606 :cpp:class:`pw::rpc::ClientReaderWriter`, etc.) inside the :cpp:func:`Send` 1607 function or any descendent calls. Doing so will result in deadlock! RPC APIs 1608 may be used by other threads, just not within :cpp:func:`Send`. 1609 1610 The buffer provided in ``packet`` must NOT be accessed outside of this 1611 function. It must be sent immediately or copied elsewhere before the 1612 function returns. 1613