@@ -37,19 +37,8 @@ class FileDownloader final
3737 return FileDownloader{presentation, time_provider};
3838 }
3939
40- FileDownloader (FileDownloader&& other) noexcept
41- : presentation_{other.presentation_ }
42- , time_provider_{other.time_provider_ }
43- , get_info_client_{std::move (other.get_info_client_ )}
44- , get_info_promise_{std::move (other.get_info_promise_ )}
45- , read_client_{std::move (other.read_client_ )}
46- , read_promise_{std::move (other.read_promise_ )}
47- , read_request_{std::move (other.read_request_ )}
48- , file_stats_{other.file_stats_ }
49- {
50- }
51-
52- ~FileDownloader () = default ;
40+ FileDownloader (FileDownloader&& other) noexcept = default ;
41+ ~FileDownloader () = default ;
5342
5443 FileDownloader (const FileDownloader&) = delete ;
5544 FileDownloader& operator =(const FileDownloader&) = delete ;
@@ -62,33 +51,25 @@ class FileDownloader final
6251 read_promise_.reset ();
6352 read_client_.reset ();
6453
65- file_stats_.file_size = 0 ;
66- file_stats_.file_progress = 0 ;
67- file_stats_.file_error .value = uavcan::file::Error_1_0::OK;
54+ state_.file_size = 0 ;
55+ state_.file_progress = 0 ;
56+ state_.file_path = file_path;
57+ state_.start_time = time_provider_.now ();
58+ state_.file_error .value = uavcan::file::Error_1_0::OK;
6859
6960 get_info_client_ = makeClient<Svc::GetInfo>(" GetInfo" , remote_node_id);
7061 read_client_ = makeClient<Svc::Read>(" Read" , remote_node_id);
7162 if (!get_info_client_ || !read_client_)
7263 {
73- file_stats_ .file_error .value = uavcan::file::Error_1_0::UNKNOWN_ERROR;
64+ state_ .file_error .value = uavcan::file::Error_1_0::UNKNOWN_ERROR;
7465 return false ;
7566 }
7667
7768 read_request_.offset = 0 ;
78- file_stats_.start_time = time_provider_.now ();
7969 read_request_.path .path = {file_path.begin (), file_path.end (), &presentation_.memory ()};
8070
8171 std::cout << " Getting file info (path='" << file_path << " ')...\n " ;
82- Svc::GetInfo::Request gi_request{&presentation_.memory ()};
83- gi_request.path .path = {file_path.begin (), file_path.end (), &presentation_.memory ()};
84- return makeRequest<Svc::GetInfo>(" GetInfo" ,
85- get_info_client_,
86- get_info_promise_,
87- gi_request,
88- [this ](const auto & arg) {
89- //
90- handleGetInfoPromiseResult (arg.result );
91- });
72+ return initiateGetInfoRequest ();
9273 }
9374
9475private:
@@ -111,14 +92,16 @@ class FileDownloader final
11192
11293 }; // Svc
11394
114- struct FileStats
95+ struct State
11596 {
97+ std::string file_path;
11698 uavcan::file::Error_1_0 file_error;
11799 libcyphal::TimePoint start_time;
118100 std::size_t file_size{0 };
119101 std::size_t file_progress{0 };
102+ int failed_requests{0 };
120103
121- }; // FileStats
104+ }; // State
122105
123106 FileDownloader (Presentation& presentation, libcyphal::ITimeProvider& time_provider)
124107 : presentation_{presentation}
@@ -155,7 +138,7 @@ class FileDownloader final
155138 if (const auto * const failure = cetl::get_if<typename Service::Failure>(&maybe_promise))
156139 {
157140 (void ) failure;
158- file_stats_ .file_error .value = uavcan::file::Error_1_0::UNKNOWN_ERROR;
141+ state_ .file_error .value = uavcan::file::Error_1_0::UNKNOWN_ERROR;
159142 std::cerr << " Can't make '" << role << " ' request.\n " ;
160143 complete ();
161144 return false ;
@@ -166,40 +149,64 @@ class FileDownloader final
166149 return true ;
167150 }
168151
152+ bool initiateGetInfoRequest ()
153+ {
154+ Svc::GetInfo::Request request{&presentation_.memory ()};
155+ request.path .path = {state_.file_path .begin (), state_.file_path .end (), &presentation_.memory ()};
156+ return makeRequest<Svc::GetInfo>(" GetInfo" ,
157+ get_info_client_,
158+ get_info_promise_,
159+ request,
160+ [this ](const auto & arg) {
161+ //
162+ handleGetInfoPromiseResult (arg.result );
163+ });
164+ }
165+
169166 void handleGetInfoPromiseResult (const Svc::GetInfo::Promise::Result& result)
170167 {
171168 if (const auto * const failure = cetl::get_if<ResponsePromiseFailure>(&result))
172169 {
173170 (void ) failure;
174- file_stats_.file_error .value = uavcan::file::Error_1_0::UNKNOWN_ERROR;
175- std::cerr << " GetInfo request failed.\n " ;
176- complete ();
171+ state_.failed_requests ++;
172+ if (state_.failed_requests >= MaxRetriesOnRequestFailure)
173+ {
174+ std::cerr << " 'GetInfo' request failed (times=" << state_.failed_requests << " ).\n " ;
175+ state_.file_error .value = uavcan::file::Error_1_0::UNKNOWN_ERROR;
176+ complete ();
177+ }
178+ else
179+ {
180+ std::cerr << " 'GetInfo' request failed (times=" << state_.failed_requests << " ). Retrying…\n " ;
181+ initiateGetInfoRequest ();
182+ }
177183 return ;
178184 }
179- const auto success = cetl::get<Svc::GetInfo::Promise::Success>(result);
185+ state_.failed_requests = 0 ;
186+ const auto success = cetl::get<Svc::GetInfo::Promise::Success>(result);
180187
181188 get_info_promise_.reset ();
182189 get_info_client_.reset ();
183190
184191 const auto & response = success.response ;
185192 if (response._error .value == uavcan::file::Error_1_0::OK)
186193 {
187- file_stats_ .file_size = response.size ;
188- std::cout << " Downloading (size=" << file_stats_ .file_size << " ) ...\n " ;
189- if (file_stats_ .file_size > 0 )
194+ state_ .file_size = response.size ;
195+ std::cout << " Downloading (size=" << state_ .file_size << " ) ...\n " ;
196+ if (state_ .file_size > 0 )
190197 {
191- file_stats_ .start_time = time_provider_.now ();
198+ state_ .start_time = time_provider_.now ();
192199
193200 printProgress ();
194201 initiateNextReadRequest ();
195202 return ;
196203 }
197204
198- file_stats_ .file_error .value = uavcan::file::Error_1_0::OK;
205+ state_ .file_error .value = uavcan::file::Error_1_0::OK;
199206 }
200207 else
201208 {
202- file_stats_ .file_error = response._error ;
209+ state_ .file_error = response._error ;
203210 std::cerr << " Can't get file info (err=" << response._error .value << " ).\n " ;
204211 }
205212
@@ -219,11 +226,22 @@ class FileDownloader final
219226 if (const auto * const failure = cetl::get_if<ResponsePromiseFailure>(&result))
220227 {
221228 (void ) failure;
222- file_stats_.file_error .value = uavcan::file::Error_1_0::UNKNOWN_ERROR;
223- std::cerr << " Read request failed.\n " ;
224- complete ();
229+ state_.failed_requests ++;
230+ if (state_.failed_requests >= MaxRetriesOnRequestFailure)
231+ {
232+ std::cerr << " 'Read' request failed (times=" << state_.failed_requests << " ).\n " ;
233+ state_.file_error .value = uavcan::file::Error_1_0::UNKNOWN_ERROR;
234+ complete ();
235+ }
236+ else
237+ {
238+ std::cerr << " 'Read' request failed (times=" << state_.failed_requests << " ). Retrying…\n " ;
239+ initiateNextReadRequest ();
240+ }
225241 return ;
226242 }
243+ state_.failed_requests = 0 ;
244+
227245 const auto success = cetl::get<Svc::Read::Promise::Success>(std::move (result));
228246
229247 const auto & response = success.response ;
@@ -243,25 +261,25 @@ class FileDownloader final
243261 }
244262 else
245263 {
246- file_stats_ .file_error = response._error ;
264+ state_ .file_error = response._error ;
247265 std::cerr << " Can't read file (err=" << response._error .value << " ).\n " ;
248266 }
249267 complete ();
250268 }
251269
252270 void printProgress ()
253271 {
254- CETL_DEBUG_ASSERT (file_stats_ .file_size > 0 , " " );
255- CETL_DEBUG_ASSERT (read_request_.offset <= file_stats_ .file_size , " " );
272+ CETL_DEBUG_ASSERT (state_ .file_size > 0 , " " );
273+ CETL_DEBUG_ASSERT (read_request_.offset <= state_ .file_size , " " );
256274
257- const auto progress = (read_request_.offset * 100U ) / file_stats_ .file_size ;
275+ const auto progress = (read_request_.offset * 100U ) / state_ .file_size ;
258276 CETL_DEBUG_ASSERT (progress <= 100U , " " );
259277
260278 // Print progress only if its integer % has changed (or in the beginning).
261- if ((progress != file_stats_ .file_progress ) || (read_request_.offset == 0 ))
279+ if ((progress != state_ .file_progress ) || (read_request_.offset == 0 ))
262280 {
263- file_stats_ .file_progress = progress;
264- const auto duration = time_provider_.now () - file_stats_ .start_time ;
281+ state_ .file_progress = progress;
282+ const auto duration = time_provider_.now () - state_ .start_time ;
265283 if (const auto duration_us = std::chrono::duration_cast<std::chrono::microseconds>(duration).count ())
266284 {
267285 const auto speed_kb_per_sec = (read_request_.offset * 1000000U ) / (duration_us * 1024U );
@@ -277,9 +295,9 @@ class FileDownloader final
277295
278296 void complete ()
279297 {
280- const auto duration = time_provider_.now () - file_stats_ .start_time ;
281- std::cout << " \n Download completed (err=" << file_stats_ .file_error .value //
282- << " , time=" << std::fixed << std::setprecision (6 ) // NOLINT
298+ const auto duration = time_provider_.now () - state_ .start_time ;
299+ std::cout << " \n Download completed (err=" << state_ .file_error .value //
300+ << " , time=" << std::fixed << std::setprecision (6 ) // NOLINT
283301 << std::chrono::duration_cast<std::chrono::duration<double >>(duration).count () << " s).\n "
284302 << std::flush;
285303
@@ -291,14 +309,16 @@ class FileDownloader final
291309
292310 // MARK: Data members:
293311
312+ static constexpr int MaxRetriesOnRequestFailure = 10 ;
313+
294314 Presentation& presentation_;
295315 libcyphal::ITimeProvider& time_provider_;
296316 cetl::optional<Svc::GetInfo::Client> get_info_client_;
297317 cetl::optional<Svc::GetInfo::Promise> get_info_promise_;
298318 cetl::optional<Svc::Read::Client> read_client_;
299319 cetl::optional<Svc::Read::Promise> read_promise_;
300320 Svc::Read::Request read_request_{&presentation_.memory ()};
301- FileStats file_stats_ ;
321+ State state_ ;
302322
303323}; // FileDownloader
304324
0 commit comments