@@ -37,19 +37,8 @@ class FileDownloader final
37
37
return FileDownloader{presentation, time_provider};
38
38
}
39
39
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 ;
53
42
54
43
FileDownloader (const FileDownloader&) = delete ;
55
44
FileDownloader& operator =(const FileDownloader&) = delete ;
@@ -62,33 +51,25 @@ class FileDownloader final
62
51
read_promise_.reset ();
63
52
read_client_.reset ();
64
53
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;
68
59
69
60
get_info_client_ = makeClient<Svc::GetInfo>(" GetInfo" , remote_node_id);
70
61
read_client_ = makeClient<Svc::Read>(" Read" , remote_node_id);
71
62
if (!get_info_client_ || !read_client_)
72
63
{
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;
74
65
return false ;
75
66
}
76
67
77
68
read_request_.offset = 0 ;
78
- file_stats_.start_time = time_provider_.now ();
79
69
read_request_.path .path = {file_path.begin (), file_path.end (), &presentation_.memory ()};
80
70
81
71
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 ();
92
73
}
93
74
94
75
private:
@@ -111,14 +92,16 @@ class FileDownloader final
111
92
112
93
}; // Svc
113
94
114
- struct FileStats
95
+ struct State
115
96
{
97
+ std::string file_path;
116
98
uavcan::file::Error_1_0 file_error;
117
99
libcyphal::TimePoint start_time;
118
100
std::size_t file_size{0 };
119
101
std::size_t file_progress{0 };
102
+ int failed_requests{0 };
120
103
121
- }; // FileStats
104
+ }; // State
122
105
123
106
FileDownloader (Presentation& presentation, libcyphal::ITimeProvider& time_provider)
124
107
: presentation_{presentation}
@@ -155,7 +138,7 @@ class FileDownloader final
155
138
if (const auto * const failure = cetl::get_if<typename Service::Failure>(&maybe_promise))
156
139
{
157
140
(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;
159
142
std::cerr << " Can't make '" << role << " ' request.\n " ;
160
143
complete ();
161
144
return false ;
@@ -166,40 +149,64 @@ class FileDownloader final
166
149
return true ;
167
150
}
168
151
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
+
169
166
void handleGetInfoPromiseResult (const Svc::GetInfo::Promise::Result& result)
170
167
{
171
168
if (const auto * const failure = cetl::get_if<ResponsePromiseFailure>(&result))
172
169
{
173
170
(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
+ }
177
183
return ;
178
184
}
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);
180
187
181
188
get_info_promise_.reset ();
182
189
get_info_client_.reset ();
183
190
184
191
const auto & response = success.response ;
185
192
if (response._error .value == uavcan::file::Error_1_0::OK)
186
193
{
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 )
190
197
{
191
- file_stats_ .start_time = time_provider_.now ();
198
+ state_ .start_time = time_provider_.now ();
192
199
193
200
printProgress ();
194
201
initiateNextReadRequest ();
195
202
return ;
196
203
}
197
204
198
- file_stats_ .file_error .value = uavcan::file::Error_1_0::OK;
205
+ state_ .file_error .value = uavcan::file::Error_1_0::OK;
199
206
}
200
207
else
201
208
{
202
- file_stats_ .file_error = response._error ;
209
+ state_ .file_error = response._error ;
203
210
std::cerr << " Can't get file info (err=" << response._error .value << " ).\n " ;
204
211
}
205
212
@@ -219,11 +226,22 @@ class FileDownloader final
219
226
if (const auto * const failure = cetl::get_if<ResponsePromiseFailure>(&result))
220
227
{
221
228
(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
+ }
225
241
return ;
226
242
}
243
+ state_.failed_requests = 0 ;
244
+
227
245
const auto success = cetl::get<Svc::Read::Promise::Success>(std::move (result));
228
246
229
247
const auto & response = success.response ;
@@ -243,25 +261,25 @@ class FileDownloader final
243
261
}
244
262
else
245
263
{
246
- file_stats_ .file_error = response._error ;
264
+ state_ .file_error = response._error ;
247
265
std::cerr << " Can't read file (err=" << response._error .value << " ).\n " ;
248
266
}
249
267
complete ();
250
268
}
251
269
252
270
void printProgress ()
253
271
{
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 , " " );
256
274
257
- const auto progress = (read_request_.offset * 100U ) / file_stats_ .file_size ;
275
+ const auto progress = (read_request_.offset * 100U ) / state_ .file_size ;
258
276
CETL_DEBUG_ASSERT (progress <= 100U , " " );
259
277
260
278
// 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 ))
262
280
{
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 ;
265
283
if (const auto duration_us = std::chrono::duration_cast<std::chrono::microseconds>(duration).count ())
266
284
{
267
285
const auto speed_kb_per_sec = (read_request_.offset * 1000000U ) / (duration_us * 1024U );
@@ -277,9 +295,9 @@ class FileDownloader final
277
295
278
296
void complete ()
279
297
{
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
283
301
<< std::chrono::duration_cast<std::chrono::duration<double >>(duration).count () << " s).\n "
284
302
<< std::flush;
285
303
@@ -291,14 +309,16 @@ class FileDownloader final
291
309
292
310
// MARK: Data members:
293
311
312
+ static constexpr int MaxRetriesOnRequestFailure = 10 ;
313
+
294
314
Presentation& presentation_;
295
315
libcyphal::ITimeProvider& time_provider_;
296
316
cetl::optional<Svc::GetInfo::Client> get_info_client_;
297
317
cetl::optional<Svc::GetInfo::Promise> get_info_promise_;
298
318
cetl::optional<Svc::Read::Client> read_client_;
299
319
cetl::optional<Svc::Read::Promise> read_promise_;
300
320
Svc::Read::Request read_request_{&presentation_.memory ()};
301
- FileStats file_stats_ ;
321
+ State state_ ;
302
322
303
323
}; // FileDownloader
304
324
0 commit comments