sheaf · fragmentation scan · pw_rpc · 2026-05-29 03:43 UTC

21 Classes

Concept documentation
24%
5 with doc evidence
show the 5
Tests
33%
7 with test evidence
show the 7

125 Functions

Concept documentation
5%
6 with doc evidence
show the 6
Tests
6%
7 with test evidence
show the 7

139 Gaps · Worklist

Concept docs cover 11 functions; tests cover 14. Concept Docs is the bottleneck — every Completed function needs both. Closing the largest group would move 90% of the debt — the biggest single shift available.

125
Unclaimed
DOCS TESTS
only the contract, nothing else
pw::internal::ConvertToStatuspw::internal::ConvertToValuepw::rpc::BenchmarkService::BidirectionalEchopw::rpc::BenchmarkService::UnaryEchopw::rpc::Channel::Createpw::rpc::ChannelOutput::MaximumTransmissionUnit
12
Exercised
DOCS TESTS
tested, but no prose names it
pw::rpc::BenchmarkServicepw::rpc::Channelpw::rpc::Channel::Channelpw::rpc::ChannelOutput::ChannelOutputpw::rpc::Client::Clientpw::rpc::ClientServer
9
Asserted
DOCS TESTS
claim in docs, nothing verifies it
pw::rpc::ChangeEncodedChannelIdpw::rpc::ChannelOutput::Sendpw::rpc::Serverpw::rpc::Server::ProcessPacketpw::rpc::SynchronousCallpw::rpc::SynchronousCallFor
2
Completed
DOCS TESTS
concept docs and tests aligned
pw::rpc::ChannelOutputpw::rpc::Client
5
NAME THE CALL Add the identifier to the prose so grep and embeddings can resolve the name.
125
REPLACE THE STUB Replace the inherited boilerplate with prose that explains the actual behavior.
pw::rpc::BenchmarkService::BidirectionalEcho pw_rpc/public/pw_rpc/benchmark.h:31
pw::rpc::BenchmarkService::UnaryEcho pw_rpc/public/pw_rpc/benchmark.h:29
pw::rpc::Channel::Create pw_rpc/public/pw_rpc/channel.h:238
pw::rpc::ChannelOutput::MaximumTransmissionUnit pw_rpc/public/pw_rpc/channel.h:113
pw::rpc::ChannelOutput::max pw_rpc/public/pw_rpc/channel.h:100
pw::rpc::ChannelOutput::name pw_rpc/public/pw_rpc/channel.h:108
pw::rpc::Client::ProcessPacket pw_rpc/public/pw_rpc/client.h:49
pw::rpc::ClientServer::ProcessPacket pw_rpc/public/pw_rpc/client_server.h:37
pw::rpc::ClientServer::client pw_rpc/public/pw_rpc/client_server.h:39
pw::rpc::ClientServer::server pw_rpc/public/pw_rpc/client_server.h:40
pw::rpc::GetServiceIdForMethod pw_rpc/public/pw_rpc/method_info.h:74
pw::rpc::HasClientStream pw_rpc/public/pw_rpc/method_type.h:34
pw::rpc::HasServerStream pw_rpc/public/pw_rpc/method_type.h:28
pw::rpc::PacketMeta::FromBuffer pw_rpc/public/pw_rpc/packet_meta.h:36
pw::rpc::PacketMeta::channel_id pw_rpc/public/pw_rpc/packet_meta.h:37
pw::rpc::PacketMeta::destination_is_client pw_rpc/public/pw_rpc/packet_meta.h:40
pw::rpc::PacketMeta::destination_is_server pw_rpc/public/pw_rpc/packet_meta.h:43
pw::rpc::PacketMeta::method_id pw_rpc/public/pw_rpc/packet_meta.h:39
pw::rpc::PacketMeta::payload pw_rpc/public/pw_rpc/packet_meta.h:54
pw::rpc::PacketMeta::service_id pw_rpc/public/pw_rpc/packet_meta.h:38
pw::rpc::PacketMeta::type_is_client_error pw_rpc/public/pw_rpc/packet_meta.h:46
pw::rpc::PacketMeta::type_is_server_error pw_rpc/public/pw_rpc/packet_meta.h:49
pw::rpc::PayloadsView::advance pw_rpc/public/pw_rpc/payloads_view.h:107
pw::rpc::PayloadsView::back pw_rpc/public/pw_rpc/payloads_view.h:118
pw::rpc::PayloadsView::begin pw_rpc/public/pw_rpc/payloads_view.h:106
pw::rpc::PayloadsView::empty pw_rpc/public/pw_rpc/payloads_view.h:114
pw::rpc::PayloadsView::end pw_rpc/public/pw_rpc/payloads_view.h:121
pw::rpc::PayloadsView::front pw_rpc/public/pw_rpc/payloads_view.h:117
pw::rpc::PayloadsView::iterator pw_rpc/public/pw_rpc/payloads_view.h:83
pw::rpc::PayloadsView::iterator::iterator pw_rpc/public/pw_rpc/payloads_view.h:88
pw::rpc::PayloadsView::iterator::value pw_rpc/public/pw_rpc/payloads_view.h:91
pw::rpc::PayloadsView::size pw_rpc/public/pw_rpc/payloads_view.h:112
pw::rpc::Server::IsServiceRegistered pw_rpc/public/pw_rpc/server.h:70
pw::rpc::Server::RegisterService pw_rpc/public/pw_rpc/server.h:56
pw::rpc::Server::Server pw_rpc/public/pw_rpc/server.h:41
pw::rpc::Server::UnregisterService pw_rpc/public/pw_rpc/server.h:84
pw::rpc::Service::service_id pw_rpc/public/pw_rpc/service.h:44
pw::rpc::StatusView::advance pw_rpc/public/pw_rpc/payloads_view.h:227
pw::rpc::StatusView::back pw_rpc/public/pw_rpc/payloads_view.h:237
pw::rpc::StatusView::begin pw_rpc/public/pw_rpc/payloads_view.h:226
pw::rpc::StatusView::empty pw_rpc/public/pw_rpc/payloads_view.h:233
pw::rpc::StatusView::end pw_rpc/public/pw_rpc/payloads_view.h:240
pw::rpc::StatusView::front pw_rpc/public/pw_rpc/payloads_view.h:236
pw::rpc::StatusView::iterator pw_rpc/public/pw_rpc/payloads_view.h:203
pw::rpc::StatusView::iterator::iterator pw_rpc/public/pw_rpc/payloads_view.h:208
pw::rpc::StatusView::iterator::value pw_rpc/public/pw_rpc/payloads_view.h:211
pw::rpc::StatusView::size pw_rpc/public/pw_rpc/payloads_view.h:232
pw::rpc::SynchronousCallResult::RpcError pw_rpc/public/pw_rpc/synchronous_call_result.h:77
pw::rpc::SynchronousCallResult::SynchronousCallResult pw_rpc/public/pw_rpc/synchronous_call_result.h:80
pw::rpc::SynchronousCallResult::Timeout pw_rpc/public/pw_rpc/synchronous_call_result.h:76
pw::rpc::SynchronousCallResult::is_error pw_rpc/public/pw_rpc/synchronous_call_result.h:99
pw::rpc::SynchronousCallResult::is_rpc_error pw_rpc/public/pw_rpc/synchronous_call_result.h:108
pw::rpc::SynchronousCallResult::is_server_error pw_rpc/public/pw_rpc/synchronous_call_result.h:105
pw::rpc::SynchronousCallResult::is_server_response pw_rpc/public/pw_rpc/synchronous_call_result.h:109
pw::rpc::SynchronousCallResult::is_timeout pw_rpc/public/pw_rpc/synchronous_call_result.h:107
pw::rpc::SynchronousCallResult::ok pw_rpc/public/pw_rpc/synchronous_call_result.h:102
pw::rpc::SynchronousCallResult::response pw_rpc/public/pw_rpc/synchronous_call_result.h:119
pw::rpc::SynchronousCallResult::status pw_rpc/public/pw_rpc/synchronous_call_result.h:111
pw::rpc::Writer::Write pw_rpc/public/pw_rpc/writer.h:45
pw::rpc::Writer::Writer pw_rpc/public/pw_rpc/writer.h:36
pw::rpc::Writer::active pw_rpc/public/pw_rpc/writer.h:42
pw::rpc::Writer::channel_id pw_rpc/public/pw_rpc/writer.h:43
pw::rpc::integration_test::ChannelManipulator pw_rpc/public/pw_rpc/integration_testing.h:42
pw::rpc::integration_test::ChannelManipulator::ChannelManipulator pw_rpc/public/pw_rpc/integration_testing.h:55
pw::rpc::integration_test::ChannelManipulator::ProcessAndSend pw_rpc/public/pw_rpc/integration_testing.h:72
pw::rpc::integration_test::ChannelManipulator::set_send_packet pw_rpc/public/pw_rpc/integration_testing.h:65
pw::rpc::integration_test::InitializeClient pw_rpc/public/pw_rpc/integration_testing.h:98
pw::rpc::integration_test::ProcessPackets pw_rpc/public/pw_rpc/integration_test_socket_client.h:144
pw::rpc::integration_test::SetClientSockOpt pw_rpc/public/pw_rpc/integration_testing.h:91
pw::rpc::integration_test::SetEgressChannelManipulator pw_rpc/public/pw_rpc/integration_testing.h:83
pw::rpc::integration_test::SetIngressChannelManipulator pw_rpc/public/pw_rpc/integration_testing.h:85
pw::rpc::integration_test::SocketClientContext pw_rpc/public/pw_rpc/integration_test_socket_client.h:37
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator pw_rpc/public/pw_rpc/integration_test_socket_client.h:97
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator::ChannelOutputWithManipulator pw_rpc/public/pw_rpc/integration_test_socket_client.h:99
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator::MaximumTransmissionUnit pw_rpc/public/pw_rpc/integration_test_socket_client.h:116
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator::Send pw_rpc/public/pw_rpc/integration_test_socket_client.h:119
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator::set_channel_manipulator pw_rpc/public/pw_rpc/integration_test_socket_client.h:104
pw::rpc::integration_test::SocketClientContext::SetEgressChannelManipulator pw_rpc/public/pw_rpc/integration_test_socket_client.h:75
pw::rpc::integration_test::SocketClientContext::SetIngressChannelManipulator pw_rpc/public/pw_rpc/integration_test_socket_client.h:81
pw::rpc::integration_test::SocketClientContext::SetSockOpt pw_rpc/public/pw_rpc/integration_test_socket_client.h:68
pw::rpc::integration_test::SocketClientContext::SocketClientContext pw_rpc/public/pw_rpc/integration_test_socket_client.h:39
pw::rpc::integration_test::SocketClientContext::Start pw_rpc/public/pw_rpc/integration_test_socket_client.h:51
pw::rpc::integration_test::SocketClientContext::Terminate pw_rpc/public/pw_rpc/integration_test_socket_client.h:59
pw::rpc::integration_test::SocketClientContext::client pw_rpc/public/pw_rpc/integration_test_socket_client.h:47
pw::rpc::integration_test::TerminateClient pw_rpc/public/pw_rpc/integration_testing.h:105
pw::rpc::integration_test::client pw_rpc/public/pw_rpc/integration_testing.h:88
pw::rpc::internal::ChannelBase pw_rpc/public/pw_rpc/channel.h:147
pw::rpc::internal::ChannelBase::Close pw_rpc/public/pw_rpc/channel.h:210
pw::rpc::internal::ChannelBase::Configure pw_rpc/public/pw_rpc/channel.h:160
pw::rpc::internal::ChannelBase::MaxWriteSizeBytes pw_rpc/public/pw_rpc/channel.h:218
pw::rpc::internal::ChannelBase::Send pw_rpc/public/pw_rpc/channel.h:208
pw::rpc::internal::ChannelBase::assigned pw_rpc/public/pw_rpc/channel.h:200
pw::rpc::internal::ChannelBase::id pw_rpc/public/pw_rpc/channel.h:199
pw::rpc::internal::ChannelBase::set_channel_output pw_rpc/public/pw_rpc/channel.h:190
pw::rpc::internal::OverwriteChannelId pw_rpc/public/pw_rpc/channel.h:40
pw::rpc::internal::SynchronousCallStatus pw_rpc/public/pw_rpc/synchronous_call_result.h:62
pw::rpc::internal::UnwrapMethodId pw_rpc/public/pw_rpc/method_id.h:28
pw::rpc::internal::UnwrapServiceId pw_rpc/public/pw_rpc/service_id.h:28
pw::rpc::internal::WrapMethodId pw_rpc/public/pw_rpc/method_id.h:27
pw::rpc::internal::WrapServiceId pw_rpc/public/pw_rpc/service_id.h:27
pw::rpc::internal::test::PacketFilter pw_rpc/public/pw_rpc/payloads_view.h:39
pw::rpc::internal::test::PacketFilter::PacketFilter pw_rpc/public/pw_rpc/payloads_view.h:42
pw::rpc::internal::test::PacketFilter::operator pw_rpc/public/pw_rpc/payloads_view.h:53
pw::rpc::test::SendResponseIfCalled pw_rpc/public/pw_rpc/test_helpers.h:76
pw::rpc::test::SystemClock::for_at_least pw_rpc/public/pw_rpc/test_helpers.h:101
pw::rpc::test::WaitForPackets pw_rpc/public/pw_rpc/test_helpers.h:33
7
CITE IT IN A TEST Name this element in an existing test (a t.Run label, a table-driven case name, a comment) or add a test that does.
pw::rpc::ChangeEncodedChannelId pw_rpc/public/pw_rpc/channel.h:60
pw::rpc::SynchronousCallFor pw_rpc/public/pw_rpc/synchronous_call.h:96
pw::rpc::SynchronousCallResult pw_rpc/public/pw_rpc/synchronous_call_result.h:73
pw::rpc::SynchronousCallUntil pw_rpc/public/pw_rpc/synchronous_call.h:160

Depth

Full
43%
Brief
21%
Stub
00%
None
11995%

Evidence

5
NAME THE CALL Vocabulary mismatch
pw::rpc::Channel::Channel
pw::rpc::ChannelOutput::ChannelOutput
pw::rpc::Client::Client
pw::rpc::ClientServer::ClientServer
pw::rpc::Service::Service
7
CITE IT IN A TEST Not named in any test
pw::rpc::ChangeEncodedChannelId
pw::rpc::Server
pw::rpc::SynchronousCall
pw::rpc::SynchronousCallFor
pw::rpc::SynchronousCallResult
pw::rpc::SynchronousCallUntil
pw::rpc::Writer
125
REPLACE THE STUB Signature-only — documented in name only
pw::internal::ConvertToStatus
pw::internal::ConvertToValue
pw::rpc::BenchmarkService::BidirectionalEcho
pw::rpc::BenchmarkService::UnaryEcho
pw::rpc::Channel::Create
pw::rpc::ChannelOutput::MaximumTransmissionUnit
pw::rpc::ChannelOutput::max
pw::rpc::ChannelOutput::name
pw::rpc::Client::ProcessPacket
pw::rpc::ClientServer::ProcessPacket
pw::rpc::ClientServer::client
pw::rpc::ClientServer::server
pw::rpc::GetMethodId
pw::rpc::GetServiceIdForMethod
pw::rpc::HasClientStream
pw::rpc::HasServerStream
pw::rpc::MethodId
pw::rpc::MethodSerde
pw::rpc::MethodType
pw::rpc::PacketMeta::FromBuffer
pw::rpc::PacketMeta::channel_id
pw::rpc::PacketMeta::destination_is_client
pw::rpc::PacketMeta::destination_is_server
pw::rpc::PacketMeta::method_id
pw::rpc::PacketMeta::payload
pw::rpc::PacketMeta::service_id
pw::rpc::PacketMeta::type_is_client_error
pw::rpc::PacketMeta::type_is_server_error
pw::rpc::PayloadsView
pw::rpc::PayloadsView::advance
pw::rpc::PayloadsView::back
pw::rpc::PayloadsView::begin
pw::rpc::PayloadsView::empty
pw::rpc::PayloadsView::end
pw::rpc::PayloadsView::front
pw::rpc::PayloadsView::iterator
pw::rpc::PayloadsView::iterator::iterator
pw::rpc::PayloadsView::iterator::value
pw::rpc::PayloadsView::size
pw::rpc::RpcError
pw::rpc::Server::IsServiceRegistered
pw::rpc::Server::RegisterService
pw::rpc::Server::Server
pw::rpc::Server::UnregisterService
pw::rpc::Service::service_id
pw::rpc::ServiceId
pw::rpc::StatusView
pw::rpc::StatusView::advance
pw::rpc::StatusView::back
pw::rpc::StatusView::begin
pw::rpc::StatusView::empty
pw::rpc::StatusView::end
pw::rpc::StatusView::front
pw::rpc::StatusView::iterator
pw::rpc::StatusView::iterator::iterator
pw::rpc::StatusView::iterator::value
pw::rpc::StatusView::size
pw::rpc::SynchronousCallResult::RpcError
pw::rpc::SynchronousCallResult::SynchronousCallResult
pw::rpc::SynchronousCallResult::Timeout
pw::rpc::SynchronousCallResult::is_error
pw::rpc::SynchronousCallResult::is_rpc_error
pw::rpc::SynchronousCallResult::is_server_error
pw::rpc::SynchronousCallResult::is_server_response
pw::rpc::SynchronousCallResult::is_timeout
pw::rpc::SynchronousCallResult::ok
pw::rpc::SynchronousCallResult::response
pw::rpc::SynchronousCallResult::status
pw::rpc::Timeout
pw::rpc::Writer::Write
pw::rpc::Writer::Writer
pw::rpc::Writer::active
pw::rpc::Writer::channel_id
pw::rpc::integration_test::ChannelManipulator
pw::rpc::integration_test::ChannelManipulator::ChannelManipulator
pw::rpc::integration_test::ChannelManipulator::ProcessAndSend
pw::rpc::integration_test::ChannelManipulator::set_send_packet
pw::rpc::integration_test::InitializeClient
pw::rpc::integration_test::ProcessPackets
pw::rpc::integration_test::SetClientSockOpt
pw::rpc::integration_test::SetEgressChannelManipulator
pw::rpc::integration_test::SetIngressChannelManipulator
pw::rpc::integration_test::SocketClientContext
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator::ChannelOutputWithManipulator
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator::MaximumTransmissionUnit
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator::Send
pw::rpc::integration_test::SocketClientContext::ChannelOutputWithManipulator::set_channel_manipulator
pw::rpc::integration_test::SocketClientContext::SetEgressChannelManipulator
pw::rpc::integration_test::SocketClientContext::SetIngressChannelManipulator
pw::rpc::integration_test::SocketClientContext::SetSockOpt
pw::rpc::integration_test::SocketClientContext::SocketClientContext
pw::rpc::integration_test::SocketClientContext::Start
pw::rpc::integration_test::SocketClientContext::Terminate
pw::rpc::integration_test::SocketClientContext::client
pw::rpc::integration_test::TerminateClient
pw::rpc::integration_test::client
pw::rpc::internal::ChannelBase
pw::rpc::internal::ChannelBase::Close
pw::rpc::internal::ChannelBase::Configure
pw::rpc::internal::ChannelBase::MaxWriteSizeBytes
pw::rpc::internal::ChannelBase::Send
pw::rpc::internal::ChannelBase::assigned
pw::rpc::internal::ChannelBase::id
pw::rpc::internal::ChannelBase::set_channel_output
pw::rpc::internal::OverwriteChannelId
pw::rpc::internal::SynchronousCallStatus
pw::rpc::internal::UnwrapMethodId
pw::rpc::internal::UnwrapServiceId
pw::rpc::internal::WrapMethodId
pw::rpc::internal::WrapServiceId
pw::rpc::internal::test::PacketFilter
pw::rpc::internal::test::PacketFilter::PacketFilter
pw::rpc::internal::test::PacketFilter::operator
pw::rpc::is_error
pw::rpc::is_rpc_error
pw::rpc::is_server_error
pw::rpc::is_server_response
pw::rpc::is_timeout
pw::rpc::ok
pw::rpc::response
pw::rpc::status
pw::rpc::test::SendResponseIfCalled
pw::rpc::test::SystemClock::for_at_least
pw::rpc::test::WaitForPackets
11
Out of scope
pw::rpc::BenchmarkService
pw::rpc::Channel
pw::rpc::ChannelOutput
pw::rpc::ChannelOutput::Send
pw::rpc::Client
pw::rpc::ClientServer
pw::rpc::ExtractChannelId
pw::rpc::MaxSafePayloadSize
pw::rpc::PacketMeta
pw::rpc::Server::ProcessPacket
pw::rpc::Service
CONTRACT
constexpr Status ConvertToStatus(const rpc::SynchronousCallResult<T>& result) {
  return result.status();
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr T ConvertToValue(rpc::SynchronousCallResult<T>& result) {
  return std::move(result.response());
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// RPC service with low-level RPCs for transmitting data. Used for benchmarking
// and testing.
//
// NOTE: the implementation of `BidirectionalEcho` is *not* thread-safe.
class BenchmarkService
    : public pw_rpc::raw::Benchmark::Service<BenchmarkService> {
 public:
  static void UnaryEcho(ConstByteSpan request, RawUnaryResponder& responder);

  void BidirectionalEcho(RawServerReaderWriter& reader_writer);

 private:
  using ReaderWriterId = uint64_t;
  ReaderWriterId AllocateReaderWriterId();

  ReaderWriterId next_reader_writer_id_ = 0;
DOCS

N/A

TESTS
pw_rpc/benchmark_service_test.cc:29BenchmarkService.Benchmark_UnaryEchoRequestMessage · C++ · 8 lines
TEST(BenchmarkService, Benchmark_UnaryEchoRequestMessage) {
  PW_RAW_TEST_METHOD_CONTEXT(BenchmarkService, UnaryEcho) context;
  auto msg = pw::bytes::Array<0x12, 0x34, 0x56, 0x78>();

  context.call(msg);
  EXPECT_EQ(OkStatus(), context.status());
  ASSERT_TRUE(DataEqual(context.response(), msg));
}
pw_rpc/benchmark_service_test.cc:38BenchmarkService.Benchmark_EmptyRequest · C++ · 6 lines
TEST(BenchmarkService, Benchmark_EmptyRequest) {
  PW_RAW_TEST_METHOD_CONTEXT(BenchmarkService, UnaryEcho) context;
  context.call({});
  EXPECT_EQ(OkStatus(), context.status());
  ASSERT_TRUE(DataEqual(context.response(), {}));
}
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void BidirectionalEcho(RawServerReaderWriter& reader_writer);
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
static void UnaryEcho(ConstByteSpan request, RawUnaryResponder& responder);
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
Status ChangeEncodedChannelId(ByteSpan rpc_packet) {
  static_assert(kNewChannelId < 128u,
                "Channel IDs must be less than 128 to avoid needing to "
                "re-encode the packet");
  return internal::OverwriteChannelId(rpc_packet, kNewChannelId);
}
DOCS
pw_rpc/cpp.rst:318reStructuredText · 26 words

:cpp:func:`pw::rpc::ChangeEncodedChannelId` on the encoded packet to replace the generic channel ID (``1``) with the server-side channel ID allocated when the client connected. The encoded packet is then passed to :cpp:func:`pw::rpc::Server::ProcessPacket`. - When the server sends pw_rpc packets, the :cpp:class:`pw::rpc::ChannelOutput` calls :cpp:func:`pw::rpc::ChangeEncodedChannelId` to set the channel ID back to the generic ``1``.

+ 142 more paragraphs · read full section
pw_rpc/cpp.rst:323reStructuredText · 20 words

calls :cpp:func:`pw::rpc::ChangeEncodedChannelId` to set the channel ID back to the generic ``1``.

+ 142 more paragraphs · read full section
TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Associates an ID with an interface for sending packets.
class Channel : public internal::ChannelBase {
 public:
  // Creates a channel with a static ID. The channel's output can also be
  // static, or it can set to null to allow dynamically opening connections
  // through the channel.
  template <uint32_t kId>
  constexpr static Channel Create(ChannelOutput* output) {
    static_assert(kId != kUnassignedChannelId, "Channel ID cannot be 0");
    return Channel(kId, output);
  }

  // Creates a channel with a static ID from an enum value.
DOCS

N/A

TESTS
pw_rpc/channel_test.cc:48Channel.MaxSafePayload · C++ · 30 lines
TEST(Channel, MaxSafePayload) {
  constexpr size_t kUint32Max = std::numeric_limits<uint32_t>::max();
  constexpr size_t kMaxPayloadSize = 64;

  constexpr size_t kTestPayloadSize = MaxSafePayloadSize(kMaxPayloadSize);

  // Because it's impractical to test a payload that nears the limits of a
  // uint32 varint, calculate the difference when using a smaller payload.
  constexpr size_t kPayloadSizeTestLimitations =
      varint::EncodedSize(kUint32Max) - varint::EncodedSize(kTestPayloadSize);

  // The buffer to use for encoding the RPC packet.
  std::array<std::byte, kMaxPayloadSize - kPayloadSizeTestLimitations> buffer;

  std::array<std::byte, kTestPayloadSize> payload;
  for (size_t i = 0; i < payload.size(); i++) {
    payload[i] = std::byte(i % std::numeric_limits<uint8_t>::max());
  }

  Packet packet(pwpb::PacketType::SERVER_STREAM,
                /*channel_id=*/kUint32Max,  // Varint, needs to be uint32_t max.
                /*service_id=*/42,       // Fixed-width. Value doesn't matter.
                /*method_id=*/100,       // Fixed-width. Value doesn't matter.
                /*call_id=*/kUint32Max,  // Varint, needs to be uint32_t max.
                payload,
                Status::Unauthenticated());

  Result<ConstByteSpan> result = packet.Encode(buffer);
  ASSERT_EQ(OkStatus(), result.status());
}
pw_rpc/channel_test.cc:79Channel.MaxSafePayload_OffByOne · C++ · 31 lines
TEST(Channel, MaxSafePayload_OffByOne) {
  constexpr size_t kUint32Max = std::numeric_limits<uint32_t>::max();
  constexpr size_t kMaxPayloadSize = 64;

  constexpr size_t kTestPayloadSize = MaxSafePayloadSize(kMaxPayloadSize);

  // Because it's impractical to test a payload that nears the limits of a
  // uint32 varint, calculate the difference when using a smaller payload.
  constexpr size_t kPayloadSizeTestLimitations =
      varint::EncodedSize(kUint32Max) - varint::EncodedSize(kTestPayloadSize);

  // The buffer to use for encoding the RPC packet.
  std::array<std::byte, kMaxPayloadSize - kPayloadSizeTestLimitations - 1>
      buffer;

  std::array<std::byte, kTestPayloadSize> payload;
  for (size_t i = 0; i < payload.size(); i++) {
    payload[i] = std::byte(i % std::numeric_limits<uint8_t>::max());
  }

  Packet packet(pwpb::PacketType::SERVER_STREAM,
                /*channel_id=*/kUint32Max,  // Varint, needs to be uint32_t max.
                /*service_id=*/42,       // Fixed-width. Value doesn't matter.
                /*method_id=*/100,       // Fixed-width. Value doesn't matter.
                /*call_id=*/kUint32Max,  // Varint, needs to be uint32_t max.
                payload,
                Status::Unauthenticated());

  Result<ConstByteSpan> result = packet.Encode(buffer);
  ASSERT_EQ(Status::ResourceExhausted(), result.status());
+ 1 more lines · read full file
pw_rpc/channel_test.cc:111Channel.Create_FromEnum · C++ · 6 lines
TEST(Channel, Create_FromEnum) {
  constexpr rpc::Channel one = Channel::Create<ChannelId::kOne>(nullptr);
  constexpr rpc::Channel two = Channel::Create<ChannelId::kTwo>(nullptr);
  static_assert(one.id() == 1);
  static_assert(two.id() == 2);
}
+ 1 more tests · open all in source
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Creates a dynamically assignable channel without a set ID or output.
constexpr Channel() : internal::ChannelBase(kUnassignedChannelId, nullptr) {}
DOCS

N/A

TESTS
pw_rpc/channel_test.cc:48Channel.MaxSafePayload · C++ · 30 lines
TEST(Channel, MaxSafePayload) {
  constexpr size_t kUint32Max = std::numeric_limits<uint32_t>::max();
  constexpr size_t kMaxPayloadSize = 64;

  constexpr size_t kTestPayloadSize = MaxSafePayloadSize(kMaxPayloadSize);

  // Because it's impractical to test a payload that nears the limits of a
  // uint32 varint, calculate the difference when using a smaller payload.
  constexpr size_t kPayloadSizeTestLimitations =
      varint::EncodedSize(kUint32Max) - varint::EncodedSize(kTestPayloadSize);

  // The buffer to use for encoding the RPC packet.
  std::array<std::byte, kMaxPayloadSize - kPayloadSizeTestLimitations> buffer;

  std::array<std::byte, kTestPayloadSize> payload;
  for (size_t i = 0; i < payload.size(); i++) {
    payload[i] = std::byte(i % std::numeric_limits<uint8_t>::max());
  }

  Packet packet(pwpb::PacketType::SERVER_STREAM,
                /*channel_id=*/kUint32Max,  // Varint, needs to be uint32_t max.
                /*service_id=*/42,       // Fixed-width. Value doesn't matter.
                /*method_id=*/100,       // Fixed-width. Value doesn't matter.
                /*call_id=*/kUint32Max,  // Varint, needs to be uint32_t max.
                payload,
                Status::Unauthenticated());

  Result<ConstByteSpan> result = packet.Encode(buffer);
  ASSERT_EQ(OkStatus(), result.status());
}
pw_rpc/channel_test.cc:79Channel.MaxSafePayload_OffByOne · C++ · 31 lines
TEST(Channel, MaxSafePayload_OffByOne) {
  constexpr size_t kUint32Max = std::numeric_limits<uint32_t>::max();
  constexpr size_t kMaxPayloadSize = 64;

  constexpr size_t kTestPayloadSize = MaxSafePayloadSize(kMaxPayloadSize);

  // Because it's impractical to test a payload that nears the limits of a
  // uint32 varint, calculate the difference when using a smaller payload.
  constexpr size_t kPayloadSizeTestLimitations =
      varint::EncodedSize(kUint32Max) - varint::EncodedSize(kTestPayloadSize);

  // The buffer to use for encoding the RPC packet.
  std::array<std::byte, kMaxPayloadSize - kPayloadSizeTestLimitations - 1>
      buffer;

  std::array<std::byte, kTestPayloadSize> payload;
  for (size_t i = 0; i < payload.size(); i++) {
    payload[i] = std::byte(i % std::numeric_limits<uint8_t>::max());
  }

  Packet packet(pwpb::PacketType::SERVER_STREAM,
                /*channel_id=*/kUint32Max,  // Varint, needs to be uint32_t max.
                /*service_id=*/42,       // Fixed-width. Value doesn't matter.
                /*method_id=*/100,       // Fixed-width. Value doesn't matter.
                /*call_id=*/kUint32Max,  // Varint, needs to be uint32_t max.
                payload,
                Status::Unauthenticated());

  Result<ConstByteSpan> result = packet.Encode(buffer);
  ASSERT_EQ(Status::ResourceExhausted(), result.status());
+ 1 more lines · read full file
pw_rpc/channel_test.cc:111Channel.Create_FromEnum · C++ · 6 lines
TEST(Channel, Create_FromEnum) {
  constexpr rpc::Channel one = Channel::Create<ChannelId::kOne>(nullptr);
  constexpr rpc::Channel two = Channel::Create<ChannelId::kTwo>(nullptr);
  static_assert(one.id() == 1);
  static_assert(two.id() == 2);
}
+ 1 more tests · open all in source
IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr static Channel Create(ChannelOutput* output) {
  static_assert(kId != kUnassignedChannelId, "Channel ID cannot be 0");
  return Channel(kId, output);
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
class ChannelOutput {
 public:
  // Returned from MaximumTransmissionUnit() to indicate that this ChannelOutput
  // imposes no limits on the MTU.
  static constexpr size_t kUnlimited = std::numeric_limits<size_t>::max();

  // Creates a channel output with the provided name. The name is used for
  // logging only.
  constexpr ChannelOutput(const char* name) : name_(name) {}

  virtual ~ChannelOutput() = default;
DOCS
pw_rpc/cpp.rst:312reStructuredText · 22 words

with the channel ID and a :cpp:class:`pw::rpc::ChannelOutput` associated with the new connection. - The server maintains a mapping between channel IDs and pw_rpc client connections. - On the client core, pw_rpc clients all use the same channel ID (e.g. ``1``). - As packets arrive from pw_rpc client connections, the server-side code calls :cpp:func:`pw::rpc::ChangeEncodedChannelId` on the encoded packet to replace the generic channel ID (``1``) with the server-side channel ID allocated when the client connected. The encoded packet is then passed to :cpp:func:`pw::rpc::Server::ProcessPacket`. - …

+ 142 more paragraphs · read full section
pw_rpc/cpp.rst:1108reStructuredText · 20 words

Users of ``pw_rpc`` must implement the :cpp:class:`pw::rpc::ChannelOutput` interface.

+ 7 more paragraphs · read full section
pw_rpc/cpp.rst:322reStructuredText · 18 words

- When the server sends pw_rpc packets, the :cpp:class:`pw::rpc::ChannelOutput` calls :cpp:func:`pw::rpc::ChangeEncodedChannelId` to set the channel ID back to the generic ``1``.

+ 142 more paragraphs · read full section
TESTS
pw_rpc/channel_test.cc:26ChannelOutput.Name · C++ · 10 lines
TEST(ChannelOutput, Name) {
  class NameTester : public ChannelOutput {
   public:
    NameTester(const char* name) : ChannelOutput(name) {}
    Status Send(span<const std::byte>) override { return OkStatus(); }
  };

  EXPECT_STREQ("hello_world", NameTester("hello_world").name());
  EXPECT_EQ(nullptr, NameTester(nullptr).name());
}
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Creates a channel output with the provided name. The name is used for
// logging only.
constexpr ChannelOutput(const char* name) : name_(name) {}
DOCS

N/A

TESTS
pw_rpc/channel_test.cc:26ChannelOutput.Name · C++ · 10 lines
TEST(ChannelOutput, Name) {
  class NameTester : public ChannelOutput {
   public:
    NameTester(const char* name) : ChannelOutput(name) {}
    Status Send(span<const std::byte>) override { return OkStatus(); }
  };

  EXPECT_STREQ("hello_world", NameTester("hello_world").name());
  EXPECT_EQ(nullptr, NameTester(nullptr).name());
}
IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns the maximum transmission unit that this ChannelOutput supports. If
// the ChannelOutput imposes no limit on the MTU, this function returns
// ChannelOutput::kUnlimited.
virtual size_t MaximumTransmissionUnit() { return kUnlimited; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Send() function or any descendent calls. Doing so will result in deadlock!
// RPC APIs may be used by other threads, just not within Send().
//
// The buffer provided in packet must NOT be accessed outside of this
// function. It must be sent immediately or copied elsewhere before the
// function returns.
virtual Status Send(span<const std::byte> buffer)
DOCS
pw_rpc/design.rst:95reStructuredText · 24 words

:cpp:func:`pw::rpc::ChannelOutput::Send` error.

+ 18 more paragraphs · read full section
TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returned from MaximumTransmissionUnit() to indicate that this ChannelOutput
// imposes no limits on the MTU.
static constexpr size_t kUnlimited = std::numeric_limits<size_t>::max();
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr const char* name() const { return name_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
class Client : public internal::Endpoint {
 public:
  // If dynamic allocation is supported, it is not necessary to preallocate a
  // channels list.
#if PW_RPC_DYNAMIC_ALLOCATION
  _PW_RPC_CONSTEXPR Client() = default;
#endif  // PW_RPC_DYNAMIC_ALLOCATION

  // Creates a client that uses a set of RPC channels. Channels can be shared
  // between multiple clients and servers.
  _PW_RPC_CONSTEXPR Client(span<Channel> channels) : Endpoint(channels) {}
DOCS
pw_rpc/cpp.rst:106reStructuredText · 27 words

The ``pw::rpc::Client`` class is instantiated with a list of channels that it uses to communicate. These channels can be shared with a server, but multiple clients cannot use the same channels.

+ 180 more paragraphs · read full section
pw_rpc/cpp.rst:1144reStructuredText · 17 words

MUST NOT access any RPC endpoints (:cpp:class:`pw::rpc::Client`, :cpp:class:`pw::rpc::Server`) or call objects (:cpp:class:`pw::rpc::ServerReaderWriter` :cpp:class:`pw::rpc::ClientReaderWriter`, etc.) inside the :cpp:func:`Send` function or any descendent calls. Doing so will result in deadlock! RPC APIs may be used by other threads, just not within :cpp:func:`Send`.

+ 1 more paragraphs · read full section
TESTS
pw_rpc/raw/client_test.cc:113Client.ProcessPacket_InvokesUnaryCallbacks · C++ · 14 lines
TEST(Client, ProcessPacket_InvokesUnaryCallbacks) {
  RawClientTestContext context;
  CallContext call_context = StartUnaryCall<UnaryMethod>(context);

  ASSERT_NE(call_context.completed, OkStatus());

  context.server().SendResponse<UnaryMethod>(as_bytes(span("you nary?!?")),
                                             OkStatus());

  ASSERT_NE(call_context.payload, nullptr);
  EXPECT_STREQ(call_context.payload, "you nary?!?");
  EXPECT_EQ(call_context.completed, OkStatus());
  EXPECT_FALSE(call_context.call.active());
}
pw_rpc/raw/client_test.cc:128Client.ProcessPacket_NoCallbackSet · C++ · 11 lines
TEST(Client, ProcessPacket_NoCallbackSet) {
  RawClientTestContext context;
  CallContext call_context = StartUnaryCall<UnaryMethod>(context);
  call_context.call.set_on_completed(nullptr);

  ASSERT_NE(call_context.completed, OkStatus());

  context.server().SendResponse<UnaryMethod>(as_bytes(span("you nary?!?")),
                                             OkStatus());
  EXPECT_FALSE(call_context.call.active());
}
pw_rpc/raw/client_test.cc:140Client.ProcessPacket_InvokesStreamCallbacks · C++ · 14 lines
TEST(Client, ProcessPacket_InvokesStreamCallbacks) {
  RawClientTestContext context;
  auto call = StartStreamCall<BidirectionalStreamMethod>(context);

  context.server().SendServerStream<BidirectionalStreamMethod>(
      as_bytes(span("<=>")));

  ASSERT_NE(call.payload, nullptr);
  EXPECT_STREQ(call.payload, "<=>");

  context.server().SendResponse<BidirectionalStreamMethod>(Status::NotFound());

  EXPECT_EQ(call.completed, Status::NotFound());
}
+ 14 more tests · open all in source
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
  // If dynamic allocation is supported, it is not necessary to preallocate a
  // channels list.
#if PW_RPC_DYNAMIC_ALLOCATION
  _PW_RPC_CONSTEXPR Client() = default;
DOCS

N/A

TESTS
pw_rpc/raw/client_test.cc:113Client.ProcessPacket_InvokesUnaryCallbacks · C++ · 14 lines
TEST(Client, ProcessPacket_InvokesUnaryCallbacks) {
  RawClientTestContext context;
  CallContext call_context = StartUnaryCall<UnaryMethod>(context);

  ASSERT_NE(call_context.completed, OkStatus());

  context.server().SendResponse<UnaryMethod>(as_bytes(span("you nary?!?")),
                                             OkStatus());

  ASSERT_NE(call_context.payload, nullptr);
  EXPECT_STREQ(call_context.payload, "you nary?!?");
  EXPECT_EQ(call_context.completed, OkStatus());
  EXPECT_FALSE(call_context.call.active());
}
pw_rpc/raw/client_test.cc:128Client.ProcessPacket_NoCallbackSet · C++ · 11 lines
TEST(Client, ProcessPacket_NoCallbackSet) {
  RawClientTestContext context;
  CallContext call_context = StartUnaryCall<UnaryMethod>(context);
  call_context.call.set_on_completed(nullptr);

  ASSERT_NE(call_context.completed, OkStatus());

  context.server().SendResponse<UnaryMethod>(as_bytes(span("you nary?!?")),
                                             OkStatus());
  EXPECT_FALSE(call_context.call.active());
}
pw_rpc/raw/client_test.cc:140Client.ProcessPacket_InvokesStreamCallbacks · C++ · 14 lines
TEST(Client, ProcessPacket_InvokesStreamCallbacks) {
  RawClientTestContext context;
  auto call = StartStreamCall<BidirectionalStreamMethod>(context);

  context.server().SendServerStream<BidirectionalStreamMethod>(
      as_bytes(span("<=>")));

  ASSERT_NE(call.payload, nullptr);
  EXPECT_STREQ(call.payload, "<=>");

  context.server().SendResponse<BidirectionalStreamMethod>(Status::NotFound());

  EXPECT_EQ(call.completed, Status::NotFound());
}
+ 14 more tests · open all in source
IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
//
//   OK - The packet was processed by the client.
//   DATA_LOSS - Failed to decode the packet.
//   INVALID_ARGUMENT - The packet is intended for a server, not a client.
//   UNAVAILABLE - No RPC channel with the requested ID was found.
//
Status ProcessPacket(ConstByteSpan data)
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Class that wraps both an RPC client and a server, simplifying RPC setup when
// a device needs to function as both.
class ClientServer {
 public:
  // If dynamic allocation is supported, it is not necessary to preallocate a
  // channels list.
#if PW_RPC_DYNAMIC_ALLOCATION
  _PW_RPC_CONSTEXPR ClientServer() = default;
#endif  // PW_RPC_DYNAMIC_ALLOCATION

  _PW_RPC_CONSTEXPR ClientServer(span<Channel> channels)
      : client_(channels), server_(channels) {}

  // Sends a packet to either the client or the server, depending on its type.
DOCS

N/A

TESTS
pw_rpc/client_server_test.cc:50ClientServer.ProcessPacket_CallsServer · C++ · 15 lines
TEST(ClientServer, ProcessPacket_CallsServer) {
  ClientServer client_server(channels);
  client_server.server().RegisterService(service);

  Packet packet(pwpb::PacketType::REQUEST,
                kFakeChannelId,
                kFakeServiceId,
                kFakeMethodId,
                kFakeCallId);
  std::array<std::byte, 32> buffer;
  Result result = packet.Encode(buffer);
  EXPECT_EQ(result.status(), OkStatus());

  EXPECT_EQ(client_server.ProcessPacket(result.value()), OkStatus());
}
pw_rpc/client_server_test.cc:66ClientServer.ProcessPacket_CallsClient · C++ · 19 lines
TEST(ClientServer, ProcessPacket_CallsClient) {
  ClientServer client_server(channels);
  client_server.server().RegisterService(service);

  // Same packet as above, but type RESPONSE will skip the server and call into
  // the client.
  Packet packet(pwpb::PacketType::RESPONSE,
                kFakeChannelId,
                kFakeServiceId,
                kFakeMethodId,
                kFakeCallId);
  std::array<std::byte, 32> buffer;
  Result result = packet.Encode(buffer);
  EXPECT_EQ(result.status(), OkStatus());

  // No calls are registered on the client, so nothing should happen. The
  // ProcessPacket call still returns OK since the client handled it.
  EXPECT_EQ(client_server.ProcessPacket(result.value()), OkStatus());
}
pw_rpc/client_server_test.cc:86ClientServer.ProcessPacket_BadData · C++ · 4 lines
TEST(ClientServer, ProcessPacket_BadData) {
  ClientServer client_server(channels);
  EXPECT_EQ(client_server.ProcessPacket({}), Status::DataLoss());
}
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
  // If dynamic allocation is supported, it is not necessary to preallocate a
  // channels list.
#if PW_RPC_DYNAMIC_ALLOCATION
  _PW_RPC_CONSTEXPR ClientServer() = default;
DOCS

N/A

TESTS
pw_rpc/client_server_test.cc:50ClientServer.ProcessPacket_CallsServer · C++ · 15 lines
TEST(ClientServer, ProcessPacket_CallsServer) {
  ClientServer client_server(channels);
  client_server.server().RegisterService(service);

  Packet packet(pwpb::PacketType::REQUEST,
                kFakeChannelId,
                kFakeServiceId,
                kFakeMethodId,
                kFakeCallId);
  std::array<std::byte, 32> buffer;
  Result result = packet.Encode(buffer);
  EXPECT_EQ(result.status(), OkStatus());

  EXPECT_EQ(client_server.ProcessPacket(result.value()), OkStatus());
}
pw_rpc/client_server_test.cc:66ClientServer.ProcessPacket_CallsClient · C++ · 19 lines
TEST(ClientServer, ProcessPacket_CallsClient) {
  ClientServer client_server(channels);
  client_server.server().RegisterService(service);

  // Same packet as above, but type RESPONSE will skip the server and call into
  // the client.
  Packet packet(pwpb::PacketType::RESPONSE,
                kFakeChannelId,
                kFakeServiceId,
                kFakeMethodId,
                kFakeCallId);
  std::array<std::byte, 32> buffer;
  Result result = packet.Encode(buffer);
  EXPECT_EQ(result.status(), OkStatus());

  // No calls are registered on the client, so nothing should happen. The
  // ProcessPacket call still returns OK since the client handled it.
  EXPECT_EQ(client_server.ProcessPacket(result.value()), OkStatus());
}
pw_rpc/client_server_test.cc:86ClientServer.ProcessPacket_BadData · C++ · 4 lines
TEST(ClientServer, ProcessPacket_BadData) {
  ClientServer client_server(channels);
  EXPECT_EQ(client_server.ProcessPacket({}), Status::DataLoss());
}
IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Sends a packet to either the client or the server, depending on its type.
Status ProcessPacket(ConstByteSpan packet);
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr Client& client() { return client_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr Server& server() { return server_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
/// Extracts the channel ID from a pw_rpc packet.
///
/// @returns @Result{the channel ID}
/// * @DATA_LOSS: The packet is corrupt and the channel ID could not be found.
Result<uint32_t> ExtractChannelId(ConstByteSpan packet);
DOCS

N/A

TESTS
pw_rpc/channel_test.cc:122ExtractChannelId.ValidPacket · C++ · 9 lines
TEST(ExtractChannelId, ValidPacket) {
  std::byte buffer[64] = {};
  Result<ConstByteSpan> result = kTestPacket.Encode(buffer);
  ASSERT_EQ(result.status(), OkStatus());

  Result<uint32_t> channel_id = ExtractChannelId(*result);
  ASSERT_EQ(channel_id.status(), OkStatus());
  EXPECT_EQ(*channel_id, 23u);
}
pw_rpc/channel_test.cc:132ExtractChannelId.InvalidPacket · C++ · 7 lines
TEST(ExtractChannelId, InvalidPacket) {
  constexpr std::byte buffer[64] = {std::byte{1}, std::byte{2}};

  Result<uint32_t> channel_id = ExtractChannelId(buffer);

  EXPECT_EQ(channel_id.status(), Status::DataLoss());
}
pw_rpc/channel_test.cc:140ExtractChannelId.ChangeChannelIdToValidValue · C++ · 29 lines
TEST(ExtractChannelId, ChangeChannelIdToValidValue) {
  std::byte buffer[64] = {};
  Result<ConstByteSpan> result = kTestPacket.Encode(buffer);
  ByteSpan packet(buffer, result.value().size());

  EXPECT_EQ(ChangeEncodedChannelId(packet, 0), OkStatus());
  EXPECT_EQ(ExtractChannelId(packet).value(), 0u);

  EXPECT_EQ(ChangeEncodedChannelId(packet, 1), OkStatus());
  EXPECT_EQ(ExtractChannelId(packet).value(), 1u);

  EXPECT_EQ(ChangeEncodedChannelId(packet, 23), OkStatus());
  EXPECT_EQ(ExtractChannelId(packet).value(), 23u);

  EXPECT_EQ(ChangeEncodedChannelId(packet, 127), OkStatus());
  EXPECT_EQ(ExtractChannelId(packet).value(), 127u);

  EXPECT_EQ(ChangeEncodedChannelId<0>(packet), OkStatus());
  EXPECT_EQ(ExtractChannelId(packet).value(), 0u);

  EXPECT_EQ(ChangeEncodedChannelId<1>(packet), OkStatus());
  EXPECT_EQ(ExtractChannelId(packet).value(), 1u);

  EXPECT_EQ(ChangeEncodedChannelId<23>(packet), OkStatus());
  EXPECT_EQ(ExtractChannelId(packet).value(), 23u);

  EXPECT_EQ(ChangeEncodedChannelId<127>(packet), OkStatus());
  EXPECT_EQ(ExtractChannelId(packet).value(), 127u);
}
+ 2 more tests · open all in source
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr MethodId GetMethodId() {
  return internal::WrapMethodId(internal::MethodInfo<kMethod>::kMethodId);
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr ServiceId GetServiceIdForMethod() {
  return internal::WrapServiceId(internal::MethodInfo<kMethod>::kServiceId);
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// True for client and bidirectional streaming RPCs.
constexpr bool HasClientStream(MethodType type) {
  return (static_cast<unsigned>(type) &
          static_cast<unsigned>(MethodType::kClientStreaming)) != 0;
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// True for unary and server streaming RPCs.
constexpr bool HasServerStream(MethodType type) {
  return (static_cast<unsigned>(type) &
          static_cast<unsigned>(MethodType::kServerStreaming)) != 0;
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
/// determine the largest supported payload, even when dynamic allocation is
/// enabled.
///
/// @warning `MaxSafePayloadSize` does NOT account for the channel MTU, which
/// may be smaller. Call `MaxWriteSizeBytes()` on an RPC's call object
/// (reader/writer) to account for channel MTU.
constexpr size_t MaxSafePayloadSize(
    size_t encode_buffer_size = cfg::kEncodingBufferSizeBytes) {
  return encode_buffer_size > internal::Packet::kMinEncodedSizeWithoutPayload
             ? encode_buffer_size -
                   internal::Packet::kMinEncodedSizeWithoutPayload
             : 0;
}
DOCS

N/A

TESTS
pw_rpc/raw/client_reader_writer_test.cc:642RawClientWriter.MaxSafePayloadSize_UnlimitedChannel · C++ · 10 lines
TEST(RawClientWriter, MaxSafePayloadSize_UnlimitedChannel) {
  RawClientTestContext ctx;
  RawClientWriter call = TestService::TestClientStreamRpc(
      ctx.client(), ctx.channel().id(), FailIfOnCompletedCalled, FailIfCalled);

  EXPECT_EQ(call.MaxWriteSizeBytes(), pw::rpc::MaxSafePayloadSize());

  PW_TEST_EXPECT_OK(call.Cancel());
  EXPECT_EQ(call.MaxWriteSizeBytes(), 0u);
}
pw_rpc/raw/client_reader_writer_test.cc:653RawClientReaderWriter.MaxSafePayloadSize_LimitedChannel · C++ · 19 lines
TEST(RawClientReaderWriter, MaxSafePayloadSize_LimitedChannel) {
  RawClientTestContext ctx;
  RawClientReaderWriter call =
      TestService::TestBidirectionalStreamRpc(ctx.client(),
                                              ctx.channel().id(),
                                              FailIfOnNextCalled,
                                              FailIfCalled,
                                              FailIfCalled);

  constexpr size_t kChannelMtu = 40;
  static_assert(kChannelMtu < pw::rpc::MaxSafePayloadSize());

  ctx.output().set_mtu(kChannelMtu);

  EXPECT_EQ(call.MaxWriteSizeBytes(), pw::rpc::MaxSafePayloadSize(kChannelMtu));

  PW_TEST_EXPECT_OK(call.Cancel());
  EXPECT_EQ(call.MaxWriteSizeBytes(), 0u);
}
pw_rpc/raw/server_reader_writer_test.cc:647RawServerReader.MaxSafePayloadSize_UnlimitedChannel · C++ · 11 lines
TEST(RawServerReader, MaxSafePayloadSize_UnlimitedChannel) {
  ReaderWriterTestContext ctx;
  RawServerReader call =
      RawServerReader::Open<TestService::TestClientStreamRpc>(
          ctx.server, ctx.channel.id(), ctx.service);

  EXPECT_EQ(call.MaxWriteSizeBytes(), pw::rpc::MaxSafePayloadSize());

  PW_TEST_EXPECT_OK(call.Finish({}));
  EXPECT_EQ(call.MaxWriteSizeBytes(), 0u);
}
+ 1 more tests · open all in source
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// An identifier for a method.
class MethodId {
 private:
  constexpr explicit MethodId(uint32_t id) : id_(id) {}
  friend constexpr MethodId internal::WrapMethodId(uint32_t id);
  friend constexpr uint32_t internal::UnwrapMethodId(MethodId id);
  uint32_t id_;
};
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr const auto& MethodSerde() {
  return internal::MethodInfo<kMethod>::serde();
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
enum class MethodType : unsigned char {
  kUnary = 0b00,
  kServerStreaming = 0b01,
  kClientStreaming = 0b10,
  kBidirectionalStreaming = 0b11,
};
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Metadata about a `pw_rpc` packet.
//
// For now, this metadata structure only includes a limited set of information
// about the contents of a packet, but it may be extended in the future.
class PacketMeta {
 public:
  // Parses the metadata from a serialized packet.
  static Result<PacketMeta> FromBuffer(ConstByteSpan data);
  constexpr uint32_t channel_id() const { return channel_id_; }
  constexpr ServiceId service_id() const { return service_id_; }
  constexpr MethodId method_id() const { return method_id_; }
  constexpr bool destination_is_client() const {
    return destination_ == internal::Packet::kClient;
  }
  constexpr bool destination_is_server() const {
    return destination_ == internal::Packet::kServer;
DOCS

N/A

TESTS
pw_rpc/packet_meta_test.cc:46PacketMeta.FromBufferDecodesValidMinimalPacketConst · C++ · 6 lines
TEST(PacketMeta, FromBufferDecodesValidMinimalPacketConst) {
  const uint32_t kChannelId = 12;
  const uint32_t kServiceId = 0xdeadbeef;
  const uint32_t kMethodId = 44;
  FromBufferDecodesValidMinimalPacket(kChannelId, kServiceId, kMethodId);
}
pw_rpc/packet_meta_test.cc:56PacketMeta.FromBufferFailsOnIncompletePacket · C++ · 10 lines
TEST(PacketMeta, FromBufferFailsOnIncompletePacket) {
  internal::Packet packet;

  std::byte buffer[128];
  Result<ConstByteSpan> encode_result = packet.Encode(buffer);
  ASSERT_EQ(encode_result.status(), OkStatus());

  Result<PacketMeta> decode_result = PacketMeta::FromBuffer(*encode_result);
  ASSERT_EQ(decode_result.status(), Status::DataLoss());
}
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Parses the metadata from a serialized packet.
static Result<PacketMeta> FromBuffer(ConstByteSpan data);
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr uint32_t channel_id() const { return channel_id_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool destination_is_client() const {
  return destination_ == internal::Packet::kClient;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool destination_is_server() const {
  return destination_ == internal::Packet::kServer;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr MethodId method_id() const { return method_id_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Note: this `payload` is only valid so long as the original `data` buffer
// passed to `PacketMeta::FromBuffer` remains valid.
constexpr ConstByteSpan payload() const { return payload_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr ServiceId service_id() const { return service_id_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool type_is_client_error() const {
  return type_ == internal::pwpb::PacketType::CLIENT_ERROR;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool type_is_server_error() const {
  return type_ == internal::pwpb::PacketType::SERVER_ERROR;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns the payloads for a particular RPC in a Vector of RPC packets.
//
// Adapts a FilteredView of packets to return payloads instead of packets.
class PayloadsView {
 public:
  class iterator : public containers::WrappedIterator<
                       iterator,
                       internal::test::PacketsView::iterator,
                       ConstByteSpan> {
   public:
    constexpr iterator() = default;

    // Access the payload (rather than packet) with operator* and operator->.
    const ConstByteSpan& operator*() const { return value().payload(); }
    const ConstByteSpan* operator->() const { return &value().payload(); }
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
std::advance(it, index);
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
const ConstByteSpan& back() const { return *std::prev(end()); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
auto it = begin();
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
bool empty() const { return begin() == end(); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
iterator end() const { return iterator(view_.end()); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns the first/last payload for the RPC. size() must be > 0.
const ConstByteSpan& front() const { return *begin(); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
class iterator : public containers::WrappedIterator<
                     iterator,
                     internal::test::PacketsView::iterator,
                     ConstByteSpan> {
 public:
  constexpr iterator() = default;

  // Access the payload (rather than packet) with operator* and operator->.
  const ConstByteSpan& operator*() const { return value().payload(); }
  const ConstByteSpan* operator->() const { return &value().payload(); }

 private:
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr iterator() = default;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Access the payload (rather than packet) with operator* and operator->.
const ConstByteSpan& operator*() const { return value().payload(); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Number of payloads for the specified RPC.
size_t size() const { return view_.size(); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
SynchronousCallResult<Response>::RpcError(Status status) {
  return SynchronousCallResult(internal::SynchronousCallStatus::kRpc, status);
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
class Server : public internal::Endpoint {
 public:
  // If dynamic allocation is supported, it is not necessary to preallocate a
  // channels list.
#if PW_RPC_DYNAMIC_ALLOCATION
  _PW_RPC_CONSTEXPR Server() = default;
#endif  // PW_RPC_DYNAMIC_ALLOCATION

  // Creates a client that uses a set of RPC channels. Channels can be shared
  // between multiple clients and servers.
  _PW_RPC_CONSTEXPR Server(span<Channel> channels) : Endpoint(channels) {}
DOCS
pw_rpc/cpp.rst:1145reStructuredText · 13 words

:cpp:class:`pw::rpc::Server`) or call objects (:cpp:class:`pw::rpc::ServerReaderWriter` :cpp:class:`pw::rpc::ClientReaderWriter`, etc.) inside the :cpp:func:`Send` function or any descendent calls. Doing so will result in deadlock! RPC APIs may be used by other threads, just not within :cpp:func:`Send`.

+ 1 more paragraphs · read full section
TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns whether a service is registered.
//
// Calling RegisterService with a registered service will assert. So depending
// on your logic you might want to check if a service is currently registered.
bool IsServiceRegistered(const Service& service) const
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// the packet was able to be processed:
//
//   OK - The packet was processed by the server.
//   DATA_LOSS - Failed to decode the packet.
//   INVALID_ARGUMENT - The packet is intended for a client, not a server.
//   UNAVAILABLE - No RPC channel with the requested ID was found.
Status ProcessPacket(ConstByteSpan packet_data)
DOCS
pw_rpc/cpp.rst:321reStructuredText · 23 words

:cpp:func:`pw::rpc::Server::ProcessPacket`. - When the server sends pw_rpc packets, the :cpp:class:`pw::rpc::ChannelOutput` calls :cpp:func:`pw::rpc::ChangeEncodedChannelId` to set the channel ID back to the generic ``1``.

+ 142 more paragraphs · read full section
TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void RegisterService(Service& service, OtherServices&... services)
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
  // If dynamic allocation is supported, it is not necessary to preallocate a
  // channels list.
#if PW_RPC_DYNAMIC_ALLOCATION
  _PW_RPC_CONSTEXPR Server() = default;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void UnregisterService(Service& service, OtherServices&... services)
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Base class for all RPC services. This cannot be instantiated directly; use a
// generated subclass instead.
//
// Services store a span of concrete method implementation classes. To support
// different Method implementations, Service stores a base MethodUnion* and the
// size of the concrete MethodUnion object.
class Service : public IntrusiveList<Service>::Item {
 public:
  Service(const Service&) = delete;
  Service(Service&&) = delete;

  Service& operator=(const Service&) = delete;
  Service& operator=(Service&&) = delete;

  ServiceId service_id() const { return internal::WrapServiceId(id_); }

 protected:
  // Note: despite being non-`::internal` and non-`private`, this constructor
DOCS

N/A

TESTS
pw_rpc/service_test.cc:66Service.MultipleMethods_FindMethod_Present · C++ · 9 lines
TEST(Service, MultipleMethods_FindMethod_Present) {
  TestService service;
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 123),
            &TestService::kMethods[0].method());
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 456),
            &TestService::kMethods[1].method());
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 789),
            &TestService::kMethods[2].method());
}
pw_rpc/service_test.cc:76Service.MultipleMethods_FindMethod_NotPresent · C++ · 6 lines
TEST(Service, MultipleMethods_FindMethod_NotPresent) {
  TestService service;
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 0), nullptr);
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 457), nullptr);
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 999), nullptr);
}
pw_rpc/service_test.cc:89Service.NoMethods_FindMethod_NotPresent · C++ · 6 lines
TEST(Service, NoMethods_FindMethod_NotPresent) {
  EmptyTestService service;
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 123), nullptr);
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 456), nullptr);
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 789), nullptr);
}
EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
Service(const Service&) = delete;
DOCS

N/A

TESTS
pw_rpc/service_test.cc:66Service.MultipleMethods_FindMethod_Present · C++ · 9 lines
TEST(Service, MultipleMethods_FindMethod_Present) {
  TestService service;
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 123),
            &TestService::kMethods[0].method());
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 456),
            &TestService::kMethods[1].method());
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 789),
            &TestService::kMethods[2].method());
}
pw_rpc/service_test.cc:76Service.MultipleMethods_FindMethod_NotPresent · C++ · 6 lines
TEST(Service, MultipleMethods_FindMethod_NotPresent) {
  TestService service;
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 0), nullptr);
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 457), nullptr);
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 999), nullptr);
}
pw_rpc/service_test.cc:89Service.NoMethods_FindMethod_NotPresent · C++ · 6 lines
TEST(Service, NoMethods_FindMethod_NotPresent) {
  EmptyTestService service;
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 123), nullptr);
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 456), nullptr);
  EXPECT_EQ(ServiceTestHelper::FindMethod(service, 789), nullptr);
}
IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
ServiceId service_id() const { return internal::WrapServiceId(id_); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// An identifier for a service.
//
// Note: this does not identify instances of a service (Servers), only the
// service itself.
class ServiceId {
 private:
  constexpr explicit ServiceId(uint32_t id) : id_(id) {}
  friend constexpr ServiceId internal::WrapServiceId(uint32_t id);
  friend constexpr uint32_t internal::UnwrapServiceId(ServiceId id);
  uint32_t id_;
};
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Class for iterating over RPC statuses associated witha particular RPC. This
// is used to iterate over the user RPC statuses and or protocol errors for a
// particular RPC.
class StatusView {
 public:
  class iterator : public containers::WrappedIterator<
                       iterator,
                       internal::test::PacketsView::iterator,
                       Status> {
   public:
    constexpr iterator() = default;

    // Access the status (rather than packet) with operator* and operator->.
    const Status& operator*() const { return value().status(); }
    const Status* operator->() const { return &value().status(); }
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
std::advance(it, index);
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
const Status& back() const { return *std::prev(end()); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
auto it = begin();
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
bool empty() const { return begin() == end(); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
iterator end() const { return iterator(view_.end()); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns the first/last payload for the RPC. size() must be > 0.
const Status& front() const { return *begin(); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
class iterator : public containers::WrappedIterator<
                     iterator,
                     internal::test::PacketsView::iterator,
                     Status> {
 public:
  constexpr iterator() = default;

  // Access the status (rather than packet) with operator* and operator->.
  const Status& operator*() const { return value().status(); }
  const Status* operator->() const { return &value().status(); }

 private:
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr iterator() = default;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Access the status (rather than packet) with operator* and operator->.
const Status& operator*() const { return value().status(); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Number of statuses in this view.
size_t size() const { return view_.size(); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
SynchronousCallResult<Response> SynchronousCall(
    Client& client,
    uint32_t channel_id,
    const typename internal::MethodInfo<kRpcMethod>::Request& request) {
  return internal::StructSynchronousCall<kRpcMethod, Response>(
      internal::CallFreeFunctionWithCustomResponse<kRpcMethod, Response>(
          client, channel_id, request));
}
DOCS
pw_rpc/cpp.rst:623reStructuredText · 27 words

synchronous API. The :cc:`SynchronousCall\<RpcMethod\> <pw::rpc::SynchronousCall>` functions wrap the asynchronous client RPC call with a timed thread notification and returns once a result is known or a timeout has occurred. Only unary methods are supported.

+ 90 more paragraphs · read full section
TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
SynchronousCallFor(
    Client& client,
    uint32_t channel_id,
    const typename internal::MethodInfo<kRpcMethod>::Request& request,
    chrono::SystemClock::duration timeout) {
  return internal::StructSynchronousCall<kRpcMethod>(
      internal::CallFreeFunction<kRpcMethod>(client, channel_id, request),
      timeout);
}
DOCS
pw_rpc/cpp.rst:635reStructuredText · 15 words

:cc:`SynchronousCallFor\<RpcMethod\> <pw::rpc::SynchronousCallFor>` and :cc:`SynchronousCallUntil\<RpcMethod\> <pw::rpc::SynchronousCallUntil>` block for a given timeout or until a deadline, respectively. All wrappers work with either the standalone static RPC functions or the generated service client member methods.

+ 88 more paragraphs · read full section
TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
class SynchronousCallResult {
 public:
  // Error Constructors
  constexpr static SynchronousCallResult Timeout();
  constexpr static SynchronousCallResult RpcError(Status status);

  // Server Response Constructor
  constexpr explicit SynchronousCallResult(Status status, Response response)
      : call_status_(internal::SynchronousCallStatus::kServer),
        status_(status),
        response_(std::move(response)) {}
DOCS
pw_rpc/cpp.rst:628reStructuredText · 27 words

The Nanopb and pwpb APIs return a :cc:`SynchronousCallResult\<Response\> <pw::rpc::SynchronousCallResult>` object, which can be queried to determine whether any error scenarios occurred and, if not, access the response. The raw API executes a function when the call completes or returns a :cc:`Status <pw::Status>` if it does not.

+ 89 more paragraphs · read full section
TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr static SynchronousCallResult RpcError(Status status);
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Server Response Constructor
constexpr explicit SynchronousCallResult(Status status, Response response)
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Error Constructors
constexpr static SynchronousCallResult Timeout();
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns true if there was a timeout, an rpc error or the server returned a
// non-Ok status.
[[nodiscard]] constexpr bool is_error() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
[[nodiscard]] constexpr bool is_rpc_error() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns true if the server responded with a non-Ok status.
[[nodiscard]] constexpr bool is_server_error() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
[[nodiscard]] constexpr bool is_server_response() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
[[nodiscard]] constexpr bool is_timeout() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns true if the server returned a response with an Ok status.
[[nodiscard]] constexpr bool ok() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// SynchronousCallResult<Response>::response()
// SynchronousCallResult<Response>::operator*()
// SynchronousCallResult<Response>::operator->()
//
// Accessors to the held value if `this->is_server_response()`. Otherwise,
// terminates the process.
constexpr const Response& response() const&;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
[[nodiscard]] constexpr Status status() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
SynchronousCallUntil(
    Client& client,
    uint32_t channel_id,
    const typename internal::MethodInfo<kRpcMethod>::Request& request,
    chrono::SystemClock::time_point deadline) {
  return internal::StructSynchronousCall<kRpcMethod>(
      internal::CallFreeFunction<kRpcMethod>(client, channel_id, request),
      deadline);
}
DOCS
pw_rpc/cpp.rst:636reStructuredText · 19 words

:cc:`SynchronousCallUntil\<RpcMethod\> <pw::rpc::SynchronousCallUntil>` block for a given timeout or until a deadline, respectively. All wrappers work with either the standalone static RPC functions or the generated service client member methods.

+ 88 more paragraphs · read full section
TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
SynchronousCallResult<Response>::Timeout() {
  return SynchronousCallResult(internal::SynchronousCallStatus::kTimeout,
                               Status::DeadlineExceeded());
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// The Writer class allows writing requests or responses to a streaming RPC.
// ClientWriter, ClientReaderWriter, ServerWriter, and ServerReaderWriter
// classes can be used as a generic Writer.
class Writer {
 public:
  Writer(const Writer&) = delete;
  Writer(Writer&&) = delete;

  Writer& operator=(const Writer&) = delete;
  Writer& operator=(Writer&&) = delete;

  bool active() const;
  uint32_t channel_id() const;

  Status Write(ConstByteSpan payload) PW_LOCKS_EXCLUDED(internal::rpc_lock());
DOCS
pw_rpc/cpp.rst:1087reStructuredText · 25 words

``pw::rpc::Writer`` interface. On the client side, a client or bidirectional streaming RPC call object (``ClientWriter`` or ``ClientReaderWriter``) can be used as a ``pw::rpc::Writer&``. On the server side, a server or bidirectional streaming RPC call object (``ServerWriter`` or ``ServerReaderWriter``) can be used as a ``pw::rpc::Writer&``. Call ``as_writer()`` to get a ``Writer&`` of the client or server call object.

+ 10 more paragraphs · read full section
TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
Status Write(ConstByteSpan payload) PW_LOCKS_EXCLUDED(internal::rpc_lock());
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
Writer(const Writer&) = delete;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
bool active() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
uint32_t channel_id() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// server integrations may provide an opportunity to inject a
// ``ChannelManipulator`` for this use case.
//
// A ``ChannelManipulator`` should not set send_packet_, as the consumer of a
// ``ChannelManipulator`` will use ``send_packet`` to insert the provided
// ``ChannelManipulator`` into a packet processing path.
class ChannelManipulator {
 public:
  // The expected function signature of the send callback used by a
  // ChannelManipulator to forward packets to the final destination.
  //
  // The only argument is a byte span containing the RPC packet that should
  // be sent.
  //
  // Returns:
  //   OK - Packet successfully sent.
  //   Other - Failed to send packet.
  using SendCallback = Function<Status(span<const std::byte>)>;
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
ChannelManipulator()
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Processes an incoming packet before optionally sending it.
//
// Implementations of this method may send the processed packet, multiple
// packets, or no packets at all via the registered `send_packet()`
// handler.
virtual Status ProcessAndSend(span<const std::byte> packet) = 0;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Sets the true send callback that a ChannelManipulator will use to forward
// packets to the final destination.
//
// This should not be used by a ChannelManipulator. The consumer of a
// ChannelManipulator will set the send callback.
void set_send_packet(SendCallback&& send) { send_packet_ = std::move(send); }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Initializes logging and the global RPC client for integration testing. Starts
// a background thread that processes incoming.
Status InitializeClient(int argc,
                        char* argv[],
                        const char* usage_args = "PORT");
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void SocketClientContext<kMaxTransmissionUnit>::ProcessPackets() {
  constexpr size_t kDecoderBufferSize =
      hdlc::Decoder::RequiredBufferSizeForFrameSize(kMaxTransmissionUnit);
  std::array<std::byte, kDecoderBufferSize> decode_buffer;
  hdlc::Decoder decoder(decode_buffer);

  while (true) {
    std::byte byte[1];
    Result<ByteSpan> read = stream_.Read(byte);

    if (should_terminate_.test()) {
      return;
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Configure options for the socket associated with the client.
int SetClientSockOpt(int level,
                     int optname,
                     const void* optval,
                     unsigned int optlen);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void SetEgressChannelManipulator(ChannelManipulator* new_channel_manipulator);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void SetIngressChannelManipulator(ChannelManipulator* new_channel_manipulator);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
class SocketClientContext {
 public:
  constexpr SocketClientContext()
      : rpc_dispatch_thread_handle_(std::nullopt),
        channel_output_(stream_, hdlc::kDefaultRpcAddress, "socket"),
        channel_output_with_manipulator_(channel_output_),
        channel_(
            Channel::Create<kChannelId>(&channel_output_with_manipulator_)),
        client_(span(&channel_, 1)) {}

  Client& client() { return client_; }
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
class ChannelOutputWithManipulator : public ChannelOutput {
 public:
  ChannelOutputWithManipulator(ChannelOutput& actual_output)
      : ChannelOutput(actual_output.name()),
        actual_output_(actual_output),
        channel_manipulator_(nullptr) {}

  void set_channel_manipulator(ChannelManipulator* new_channel_manipulator) {
    if (new_channel_manipulator != nullptr) {
      new_channel_manipulator->set_send_packet(
          ChannelManipulator::SendCallback(
              [&](ConstByteSpan payload)
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
ChannelOutputWithManipulator(ChannelOutput& actual_output)
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
size_t MaximumTransmissionUnit() override {
  return actual_output_.MaximumTransmissionUnit();
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
Status Send(span<const std::byte> buffer) override
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void set_channel_manipulator(ChannelManipulator* new_channel_manipulator) {
  if (new_channel_manipulator != nullptr) {
    new_channel_manipulator->set_send_packet(
        ChannelManipulator::SendCallback(
            [&](ConstByteSpan payload)
                __attribute__((no_thread_safety_analysis)) {
                  return actual_output_.Send(payload);
                }));
  }
  channel_manipulator_ = new_channel_manipulator;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void SetEgressChannelManipulator(
    ChannelManipulator* new_channel_manipulator) {
  channel_output_with_manipulator_.set_channel_manipulator(
      new_channel_manipulator);
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void SetIngressChannelManipulator(
    ChannelManipulator* new_channel_manipulator) {
  if (new_channel_manipulator != nullptr) {
    new_channel_manipulator->set_send_packet([&](ConstByteSpan payload) {
      return client_.ProcessPacket(payload);
    });
  }
  ingress_channel_manipulator_ = new_channel_manipulator;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Configure options for the socket associated with the client.
int SetSockOpt(int level,
               int optname,
               const void* optval,
               unsigned int optlen) {
  return stream_.SetSockOpt(level, optname, optval, optlen);
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr SocketClientContext()
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Connects to the specified host:port and starts a background thread to read
// packets from the socket.
Status Start(const char* host, uint16_t port) {
  PW_TRY(stream_.Connect(host, port));
  rpc_dispatch_thread_handle_.emplace(&SocketClientContext::ProcessPackets,
                                      this);
  return OkStatus();
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Terminates the client, joining the RPC dispatch thread.
void Terminate() {
  PW_ASSERT(rpc_dispatch_thread_handle_.has_value());
  should_terminate_.test_and_set();
  // Close the stream to avoid blocking forever on a socket read.
  stream_.Close();
  rpc_dispatch_thread_handle_->join();
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
Client& client() { return client_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Terminates the client, joining the RPC dispatch thread.
void TerminateClient();
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns the global RPC client for integration test use.
Client& client();
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Base class for rpc::Channel with internal-only public methods, which are
// hidden in the public derived class.
class ChannelBase {
 public:
  static constexpr uint32_t kUnassignedChannelId = 0;

  // TODO: b/234876441 - Remove the Configure and set_channel_output functions.
  //     Users should call CloseChannel() / OpenChannel() to change a channel.
  //     This ensures calls are properly update and works consistently between
  //     static and dynamic channel allocation.

  // Manually configures a dynamically-assignable channel with a specified ID
  // and output. This is useful when a channel's parameters are not known until
  // runtime. This can only be called once per channel.
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr void Close() {
  PW_ASSERT(id_ != kUnassignedChannelId);
  id_ = kUnassignedChannelId;
  output_ = nullptr;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr void Configure(uint32_t id, ChannelOutput& output) {
  static_assert(
      !cfg::kDynamicAllocationEnabled<UnusedType>,
      "Configure() may not be used if PW_RPC_DYNAMIC_ALLOCATION is "
      "enabled. Call CloseChannel/OpenChannel on the endpoint instead.");
  PW_ASSERT(id_ == kUnassignedChannelId);
  PW_ASSERT(id != kUnassignedChannelId);
  id_ = id;
  output_ = &output;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Returns the maximum payload size for this channel, factoring in the
// ChannelOutput's MTU and the RPC system's `pw::rpc::MaxSafePayloadSize()`.
size_t MaxWriteSizeBytes() const;
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Invokes ChannelOutput::Send and returns its status. Any non-OK status
// indicates that the Channel is permanently closed.
Status Send(const Packet& packet) PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock());
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool assigned() const { return id_ != kUnassignedChannelId; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr uint32_t id() const { return id_; }
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr void set_channel_output(ChannelOutput& output) {
  static_assert(
      !cfg::kDynamicAllocationEnabled<UnusedType>,
      "set_channel_output() may not be used if PW_RPC_DYNAMIC_ALLOCATION is "
      "enabled. Call CloseChannel/OpenChannel on the endpoint instead.");
  PW_ASSERT(id_ != kUnassignedChannelId);
  output_ = &output;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
Status OverwriteChannelId(ByteSpan rpc_packet, uint32_t channel_id_under_128);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
enum class SynchronousCallStatus {
  kInvalid,
  kTimeout,
  kRpc,
  kServer,
};
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr uint32_t UnwrapMethodId(MethodId id);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr uint32_t UnwrapServiceId(ServiceId id);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr MethodId WrapMethodId(uint32_t id);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr ServiceId WrapServiceId(uint32_t id);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Finds packets of a specified type for a particular method.
class PacketFilter {
 public:
  // Use Channel::kUnassignedChannelId to ignore the channel.
  constexpr PacketFilter(internal::pwpb::PacketType packet_type_1,
                         internal::pwpb::PacketType packet_type_2,
                         uint32_t channel_id,
                         uint32_t service_id,
                         uint32_t method_id)
      : packet_type_1_(packet_type_1),
        packet_type_2_(packet_type_2),
        channel_id_(channel_id),
        service_id_(service_id),
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
// Use Channel::kUnassignedChannelId to ignore the channel.
constexpr PacketFilter(internal::pwpb::PacketType packet_type_1,
                       internal::pwpb::PacketType packet_type_2,
                       uint32_t channel_id,
                       uint32_t service_id,
                       uint32_t method_id)
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool operator()(const Packet& packet) const {
  return (packet.type() == packet_type_1_ ||
          packet.type() == packet_type_2_) &&
         (channel_id_ == Channel::kUnassignedChannelId ||
          packet.channel_id() == channel_id_) &&
         packet.service_id() == service_id_ &&
         packet.method_id() == method_id_;
}
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool SynchronousCallResult<Response>::is_error() const {
  return !ok();
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool SynchronousCallResult<Response>::is_rpc_error() const {
  return call_status_ == internal::SynchronousCallStatus::kRpc;
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool SynchronousCallResult<Response>::is_server_error() const {
  return is_server_response() && !status_.ok();
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool SynchronousCallResult<Response>::is_server_response() const {
  return call_status_ == internal::SynchronousCallStatus::kServer;
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool SynchronousCallResult<Response>::is_timeout() const {
  return call_status_ == internal::SynchronousCallStatus::kTimeout;
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr bool SynchronousCallResult<Response>::ok() const {
  return is_server_response() && status_.ok();
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr const Response& SynchronousCallResult<Response>::response() const& {
  PW_ASSERT(is_server_response());
  return response_;
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
constexpr Status SynchronousCallResult<Response>::status() const {
  PW_ASSERT(call_status_ != internal::SynchronousCallStatus::kInvalid);
  return status_;
}
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
Status SendResponseIfCalled(
    Context& client_context,
    const MethodResponseType<kMethod>& response,
    Status status = OkStatus(),
    chrono::SystemClock::duration timeout =
        chrono::SystemClock::for_at_least(std::chrono::milliseconds(100))) {
  const auto start_time = chrono::SystemClock::now();
  while (chrono::SystemClock::now() - start_time < timeout) {
    const auto count =
        client_context.output().template total_payloads<kMethod>();
    if (count > 0) {
      client_context.server().template SendResponse<kMethod>(response, status);
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
chrono::SystemClock::for_at_least(std::chrono::milliseconds(100))) {
DOCS

N/A

TESTS

N/A

IMPLS

N/A · no in-tree implementor

EXAMPLES

N/A

WORKFLOWS

N/A

CONTRACT
void WaitForPackets(internal::test::FakeChannelOutput& output,
                    int count,
                    Function&& run_before) {
  sync::CountingSemaphore sem;
  output.set_on_send([&sem](ConstByteSpan, Status) { sem.release(); });

  run_before();

  for (int i = 0; i < count; ++i) {
    PW_ASSERT(sem.try_acquire_for(std::chrono::seconds(kTimeoutSeconds)));
  }
DOCS

N/A

TESTS

N/A

EXAMPLES

N/A

WORKFLOWS

N/A

Workflows

Workflow coverage
No workflow corpus configured. The substance and worklist views above measure whether elements are documented, tested, and demonstrated in isolation — they do not measure whether elements appear together in any documented sequence. Configure a workflows rendered-reference block to surface this layer.
Low-coverage threshold. The red chip and matching tile border fire when a metric is at or below 20% — a figure picked because "one in five" is the folk-defensible floor at which a reasonable engineer agrees the surface is too thin to be discoverable. The threshold is absolute today; once the cross-project scan corpus crosses 100 libraries it migrates to a per-tier percentile (bottom decile fires the chip).
sheaf scanner · 148 symbols · cpp surface · 2026-05-29 03:43 UTC