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