• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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