/// <summary> /// Checks if content of FlashAir card is updated. /// </summary> /// <returns> /// True: If completed and content found updated. /// False: If completed and content found not updated. /// Null: If failed. /// </returns> private async Task <bool?> CheckUpdateAsync() { try { if (NetworkChecker.IsNetworkConnected(_card)) { OperationStatus = Resources.OperationStatus_Checking; var isUpdated = false; using (var manager = new FileManager()) { isUpdated = _card.CanGetWriteTimeStamp ? (await manager.GetWriteTimeStampAsync(CancellationToken.None) != _card.WriteTimeStamp) : await manager.CheckUpdateStatusAsync(); } OperationStatus = Resources.OperationStatus_Completed; return(isUpdated); } else { OperationStatus = Resources.OperationStatus_ConnectionUnable; return(false); } } catch (Exception ex) { if (ex is RemoteConnectionUnableException) { OperationStatus = Resources.OperationStatus_ConnectionUnable; } else if (ex is RemoteConnectionLostException) { OperationStatus = Resources.OperationStatus_ConnectionLost; } else if (ex is TimeoutException) { OperationStatus = Resources.OperationStatus_TimedOut; } else { OperationStatus = Resources.OperationStatus_Error; Debug.WriteLine($"Failed to check if content is updated.\r\n{ex}"); throw new UnexpectedException("Failed to check if content is updated.", ex); } } return(null); }
/// <summary> /// Checks if ready for operation. /// </summary> /// <returns>True if ready</returns> private bool IsReady() { if (!NetworkChecker.IsNetworkConnected()) { OperationStatus = Resources.OperationStatus_NoNetwork; return(false); } if ((Settings.Current.TargetPeriod == FilePeriod.Select) && !Settings.Current.TargetDates.Any()) { OperationStatus = Resources.OperationStatus_NoDateCalender; return(false); } return(true); }
private static async Task <byte[]> DownloadBytesAsync(HttpClient client, string path, int size, IProgress <ProgressInfo> progress, CardInfo card, CancellationToken cancellationToken) { var timeoutDuration = TimeSpan.FromSeconds(Settings.Current.TimeoutDuration); int retryCount = 0; while (true) { retryCount++; try { try { using (var response = await client.GetAsync(path, HttpCompletionOption.ResponseHeadersRead, cancellationToken)) { // If HttpResponseMessage.EnsureSuccessStatusCode is set, an exception by this setting // will be thrown in the scope of HttpClient and so cannot be caught in this method. switch (response.StatusCode) { case HttpStatusCode.OK: // None. break; case HttpStatusCode.Unauthorized: case HttpStatusCode.InternalServerError: case HttpStatusCode.BadRequest: throw new RemoteConnectionUnableException(response.StatusCode); case HttpStatusCode.NotFound: // This status code does not always mean that the specified file is missing. throw new RemoteFileNotFoundException("File is missing or request cannot be handled!", path); default: throw new HttpRequestException($"StatusCode: {response.StatusCode}"); } if ((0 < size) && (response.Content.Headers.ContentLength != size)) { throw new RemoteFileInvalidException("Data length does not match!", path); } // Because of HttpCompletionOption.ResponseHeadersRead option, neither CancellationToken // nor HttpClient.Timeout setting works for response content. // Register delegate to CancellationToken because CancellationToken can no longer // directly affect HttpClient. Disposing the HttpResponseMessage will make ReadAsStreamAsync // method throw an ObjectDisposedException and so exit this operation. var ctr = new CancellationTokenRegistration(); try { ctr = cancellationToken.Register(() => response.Dispose()); } catch (ObjectDisposedException ode) { // If CancellationTokenSource has been disposed during operation (it unlikely happens), // this exception will be thrown. Debug.WriteLine($"CancellationTokenSource has been disposed when tried to register delegate.\r\n{ode}"); } using (ctr) { var tcs = new TaskCompletionSource <bool>(); // Start timer to monitor network connection. using (var monitorTimer = new Timer(s => { if (!NetworkChecker.IsNetworkConnected(card)) { ((TaskCompletionSource <bool>)s).TrySetResult(true); } }, tcs, _monitorInterval, _monitorInterval)) { var monitorTask = tcs.Task; if ((size == 0) || (progress == null)) { // Route without progress reporting var readTask = Task.Run(async() => await response.Content.ReadAsByteArrayAsync()); var timeoutTask = Task.Delay(timeoutDuration); var completedTask = await Task.WhenAny(readTask, timeoutTask, monitorTask); if (completedTask == timeoutTask) { throw new TimeoutException("Reading response content timed out!"); } if (completedTask == monitorTask) { throw new RemoteConnectionLostException("Connection lost!"); } var bytes = await readTask; if ((0 < size) && (bytes.Length != size)) { throw new RemoteFileInvalidException("Data length does not match!", path); } return(bytes); } else { // Route with progress reporting int readLength; int readLengthTotal = 0; var buffer = new byte[65536]; // 64KiB var bufferTotal = new byte[size]; const double stepUint = 524288D; // 512KiB double stepTotal = Math.Ceiling(size / stepUint); // The number of steps to report during downloading if (stepTotal < 6) { stepTotal = 6; // The minimum number of steps } double stepCurrent = 1D; var startTime = DateTime.Now; using (var stream = await response.Content.ReadAsStreamAsync()) { while (readLengthTotal != size) { // CancellationToken in overload of ReadAsync method will not work for response content. var readTask = Task.Run(async() => await stream.ReadAsync(buffer, 0, buffer.Length)); var timeoutTask = Task.Delay(timeoutDuration); var completedTask = await Task.WhenAny(readTask, timeoutTask, monitorTask); if (completedTask == timeoutTask) { throw new TimeoutException("Reading response content timed out!"); } if (completedTask == monitorTask) { throw new RemoteConnectionLostException("Connection lost!"); } readLength = await readTask; if ((readLength == 0) || (readLengthTotal + readLength > size)) { throw new RemoteFileInvalidException("Data length does not match!", path); } Buffer.BlockCopy(buffer, 0, bufferTotal, readLengthTotal, readLength); readLengthTotal += readLength; monitorTimer.Change(_monitorInterval, _monitorInterval); // Report if read length in total exceeds stepped length. if (stepCurrent / stepTotal * size <= readLengthTotal) { progress.Report(new ProgressInfo( currentValue: readLengthTotal, totalValue: size, elapsedTime: DateTime.Now - startTime, isFirst: stepCurrent == 1D)); stepCurrent++; } } } return(bufferTotal); } } } } } catch (OperationCanceledException) // Including TaskCanceledException { if (!cancellationToken.IsCancellationRequested) { // If cancellation has not been requested, the reason of this exception must be timeout. // This is for response header only. throw new TimeoutException("Reading response header timed out!"); } throw; } catch (ObjectDisposedException) { if (cancellationToken.IsCancellationRequested) { // If cancellation has been requested, the reason of this exception must be cancellation. // This is for response content only. throw new OperationCanceledException(); } throw; } catch (IOException ie) { if (cancellationToken.IsCancellationRequested) { var we = ie.InnerException as WebException; if (we?.Status == WebExceptionStatus.ConnectionClosed) { // If cancellation has been requested during downloading, this exception may be thrown. throw new OperationCanceledException(); } } throw; } catch (HttpRequestException hre) { var we = hre.InnerException as WebException; if (we != null) { // If unable to connect to FlashAir card, this exception will be thrown. // The status may vary, such as WebExceptionStatus.NameResolutionFailure, // WebExceptionStatus.ConnectFailure. throw new RemoteConnectionUnableException(we.Status); } var ode = hre.InnerException as ObjectDisposedException; if (ode != null) { // If lost connection to FlashAir card, this exception may be thrown. // Error message: Error while copying content to a stream. throw new RemoteConnectionLostException("Connection lost!"); } throw; } } catch (RemoteConnectionUnableException) { if (retryCount >= _retryCountMax) { throw; } } catch (Exception ex) { Debug.WriteLine($"Failed to download byte array.\r\n{ex}"); throw; } // Wait interval before retry. if (TimeSpan.Zero < _retryInterval) { await Task.Delay(_retryInterval, cancellationToken); } } }