1 // Copyright 2022 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <array> 17 18 #include "pw_function/function.h" 19 #include "pw_status/status.h" 20 #include "pw_stream/stream.h" 21 #include "pw_transfer/internal/client_context.h" 22 #include "pw_transfer/internal/config.h" 23 #include "pw_transfer/transfer.raw_rpc.pb.h" 24 #include "pw_transfer/transfer_thread.h" 25 26 namespace pw::transfer { 27 28 class Client { 29 public: 30 using CompletionFunc = Function<void(Status)>; 31 32 // Initializes a transfer client on a specified RPC client and channel. 33 // Transfers are processed on a work queue so as not to block any RPC threads. 34 // The work queue does not have to be unique to the transfer client; it can be 35 // shared with other modules (including additional transfer clients). 36 // 37 // As data is processed within the work queue's context, the original RPC 38 // messages received by the transfer service are not available. Therefore, 39 // the transfer client requires an additional buffer where transfer data can 40 // stored during the context switch. 41 // 42 // The size of this buffer is the largest amount of bytes that can be sent 43 // within a single transfer chunk (read or write), excluding any transport 44 // layer overhead. Not all of this size is used to send data -- there is 45 // additional overhead in the pw_rpc and pw_transfer protocols (typically 46 // ~22B/chunk). 47 // 48 // An optional max_bytes_to_receive argument can be provided to set the 49 // default number of data bytes the client will request from the server at a 50 // time. If not provided, this defaults to the size of the data buffer. A 51 // larger value can make transfers more efficient as it minimizes the 52 // back-and-forth between client and server; however, it also increases the 53 // impact of packet loss, potentially requiring larger retransmissions to 54 // recover. 55 Client(rpc::Client& rpc_client, 56 uint32_t channel_id, 57 TransferThread& transfer_thread, 58 size_t max_bytes_to_receive = 0, 59 uint32_t extend_window_divisor = cfg::kDefaultExtendWindowDivisor) client_(rpc_client,channel_id)60 : client_(rpc_client, channel_id), 61 transfer_thread_(transfer_thread), 62 max_parameters_(max_bytes_to_receive > 0 63 ? max_bytes_to_receive 64 : transfer_thread.max_chunk_size(), 65 transfer_thread.max_chunk_size(), 66 extend_window_divisor), 67 max_retries_(cfg::kDefaultMaxRetries), 68 max_lifetime_retries_(cfg::kDefaultMaxLifetimeRetries), 69 has_read_stream_(false), 70 has_write_stream_(false) {} 71 72 // Begins a new read transfer for the given resource ID. The data read from 73 // the server is written to the provided writer. Returns OK if the transfer is 74 // successfully started. When the transfer finishes (successfully or not), the 75 // completion callback is invoked with the overall status. 76 Status Read(uint32_t resource_id, 77 stream::Writer& output, 78 CompletionFunc&& on_completion, 79 chrono::SystemClock::duration timeout = cfg::kDefaultChunkTimeout, 80 ProtocolVersion version = kDefaultProtocolVersion); 81 82 // Begins a new write transfer for the given resource ID. Data from the 83 // provided reader is sent to the server. When the transfer finishes 84 // (successfully or not), the completion callback is invoked with the overall 85 // status. 86 Status Write( 87 uint32_t resource_id, 88 stream::Reader& input, 89 CompletionFunc&& on_completion, 90 chrono::SystemClock::duration timeout = cfg::kDefaultChunkTimeout, 91 ProtocolVersion version = kDefaultProtocolVersion); 92 93 // Terminates an ongoing transfer for the specified resource ID. 94 // 95 // TODO(frolv): This should not take a resource_id, but a handle to an active 96 // transfer session. 97 void CancelTransfer(uint32_t resource_id); 98 set_extend_window_divisor(uint32_t extend_window_divisor)99 Status set_extend_window_divisor(uint32_t extend_window_divisor) { 100 if (extend_window_divisor <= 1) { 101 return Status::InvalidArgument(); 102 } 103 104 max_parameters_.set_extend_window_divisor(extend_window_divisor); 105 return OkStatus(); 106 } 107 set_max_retries(uint32_t max_retries)108 constexpr Status set_max_retries(uint32_t max_retries) { 109 if (max_retries < 1 || max_retries > max_lifetime_retries_) { 110 return Status::InvalidArgument(); 111 } 112 max_retries_ = max_retries; 113 return OkStatus(); 114 } 115 set_max_lifetime_retries(uint32_t max_lifetime_retries)116 constexpr Status set_max_lifetime_retries(uint32_t max_lifetime_retries) { 117 if (max_lifetime_retries < max_retries_) { 118 return Status::InvalidArgument(); 119 } 120 max_lifetime_retries_ = max_lifetime_retries; 121 return OkStatus(); 122 } 123 124 private: 125 static constexpr ProtocolVersion kDefaultProtocolVersion = 126 ProtocolVersion::kLatest; 127 128 using Transfer = pw_rpc::raw::Transfer; 129 130 void OnRpcError(Status status, internal::TransferType type); 131 132 Transfer::Client client_; 133 TransferThread& transfer_thread_; 134 135 internal::TransferParameters max_parameters_; 136 uint32_t max_retries_; 137 uint32_t max_lifetime_retries_; 138 139 bool has_read_stream_; 140 bool has_write_stream_; 141 }; 142 143 } // namespace pw::transfer 144