public static long CopyFileToStream(string filePath, Stream outStream, ProgressUpdateCallback progress, CancellationToken cancellationToken) { if (!File.Exists(filePath)) { throw new FileNotFoundException(nameof(filePath)); } var downloadSize = 0L; var array = new byte[32768]; using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); while (true) { cancellationToken.ThrowIfCancellationRequested(); var readSize = fileStream.Read(array, 0, array.Length); cancellationToken.ThrowIfCancellationRequested(); if (readSize <= 0) { break; } outStream.Write(array, 0, readSize); downloadSize += readSize; progress?.Invoke(new ProgressUpdateStatus(downloadSize, fileStream.Length, 0.0)); } if (downloadSize != fileStream.Length) { throw new IOException("Internal error copying streams. Total read bytes does not match stream Length."); } return(downloadSize); }
private static void RegisterProgressUpdateCallback(Action <double> whatToDo) { // Many thanks to http://www.codeproject.com/Tips/318140/How-to-make-a-callback-to-Csharp-from-C-Cplusplus // for providing a solution that worked, in terms of what calling convention to use. // I tried a few things before that - there is a lot of other (incorrect?) information // that did not work (probably my bad, still). callback = null; callback = new ProgressUpdateCallback(whatToDo); RegisterProgressUpdateCallbackNative(callback); }
public static List<uint> ByteArrayFindAll(byte[] InputBuffer, byte[] ToSearch, ProgressUpdateCallback ProgressCallback) { List<uint> Found = new List<uint>(); for (int i = 0; i < InputBuffer.Length - ToSearch.Length; i++) { if (ProgressCallback != null) { ProgressCallback((int)Math.Round(((double)i / (double)InputBuffer.Length) * 100)); } if (BComp(InputBuffer, i, ToSearch)) { Found.Add((uint)i); } } return Found; }
public Task <DownloadSummary> DownloadAsync(Uri uri, Stream outputStream, ProgressUpdateCallback progress, CancellationToken cancellationToken, IComponent?component = default, bool verify = false) { Logger.Trace($"Download requested: {uri.AbsoluteUri}"); if (outputStream == null) { throw new ArgumentNullException(nameof(outputStream)); } if (!outputStream.CanWrite) { throw new InvalidOperationException("Input stream must be writable."); } if (!uri.IsFile && !uri.IsUnc) { if (!string.Equals(uri.Scheme, "http", StringComparison.OrdinalIgnoreCase) && !string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase) && !string.Equals(uri.Scheme, "ftp", StringComparison.OrdinalIgnoreCase)) { var argumentException = new ArgumentException($"Uri scheme '{uri.Scheme}' is not supported."); Logger?.Trace($"Uri scheme '{uri.Scheme}' is not supported. {argumentException.Message}"); throw argumentException; } if (uri.AbsoluteUri.Length < 7) { var argumentException = new ArgumentException($"Invalid Uri: {uri.AbsoluteUri}."); Logger?.Trace($"The Uri is too short: {uri.AbsoluteUri}; {argumentException.Message}"); throw argumentException; } } try { var engines = GetSuitableEngines(_defaultEngines, uri); return(Task.Factory.StartNew(() => DownloadWithRetry(engines, uri, outputStream, progress, cancellationToken, component, verify), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)); } catch (Exception ex) { Logger.Trace($"Unable to get download engine: {ex.Message}"); throw; } }
private DownloadSummary DownloadWithBitRate(Uri uri, Stream outputStream, ProgressUpdateCallback progress, CancellationToken cancellationToken, IComponent?component) { var now = DateTime.Now; var lastProgressUpdate = now; ProgressUpdateCallback wrappedProgress = null; if (progress != null) { wrappedProgress = p => { var now2 = DateTime.Now; var timeSpan = now2 - lastProgressUpdate; var bitRate = 8.0 * p.BytesRead / timeSpan.TotalSeconds; progress(new ProgressUpdateStatus(p.BytesRead, p.TotalBytes, bitRate)); lastProgressUpdate = now2; } } ; var downloadSummary = DownloadCore(uri, outputStream, wrappedProgress, cancellationToken, component); downloadSummary.DownloadTime = DateTime.Now - now; downloadSummary.BitRate = 8.0 * downloadSummary.DownloadedSize / downloadSummary.DownloadTime.TotalSeconds; return(downloadSummary); }
protected override DownloadSummary DownloadCore(Uri uri, Stream outputStream, ProgressUpdateCallback progress, CancellationToken cancellationToken, IComponent?component) { if (!uri.IsFile && !uri.IsUnc) { throw new ArgumentException("Expected file or UNC path", nameof(uri)); } return(new DownloadSummary { DownloadedSize = UpdaterUtilities.CopyFileToStream(uri.LocalPath, outputStream, progress, cancellationToken) }); }
private DownloadSummary DownloadWithRetry(IDownloadEngine[] engines, Uri uri, Stream outputStream, ProgressUpdateCallback progress, CancellationToken cancellationToken, IComponent?component = null, bool verify = false) { var failureList = new List <DownloadFailureInformation>(); foreach (var engine in engines) { var position = outputStream.Position; var length = outputStream.Length; try { Logger.Trace($"Attempting download '{uri.AbsoluteUri}' using engine '{engine.Name}'"); var engineSummary = engine.Download(uri, outputStream, status => { progress?.Invoke(new ProgressUpdateStatus(engine.Name, status.BytesRead, status.TotalBytes, status.BitRate)); }, cancellationToken, component); if (outputStream.Length == 0 && !UpdateConfiguration.Instance.AllowEmptyFileDownload) { var exception = new UpdaterException($"Empty file downloaded on '{uri}'."); Logger?.Error(exception, exception.Message); throw exception; } if (verify && outputStream.Length != 0) { if (component is null) { if (UpdateConfiguration.Instance.ValidationPolicy == ValidationPolicy.Enforce) { throw new ValidationFailedException(DownloadResult.MissingOrInvalidValidationContext, "Unable to get necessary validation data because download context is null."); } } else { var componentValidationContext = component.OriginInfo?.ValidationContext; var valid = componentValidationContext?.Verify(); if ((!valid.HasValue || !valid.Value) && UpdateConfiguration.Instance.ValidationPolicy == ValidationPolicy.Enforce) { throw new ValidationFailedException(DownloadResult.MissingOrInvalidValidationContext, $"Component '{component.Name}' is missing or has an invalid ValidationInfo"); } if (valid.HasValue && valid.Value) { var validationResult = HashVerifier.Verify(outputStream, componentValidationContext); engineSummary.ValidationResult = validationResult; if (validationResult == ValidationResult.HashMismatch) { var exception = new ValidationFailedException(DownloadResult.HashMismatch, $"Hash on downloaded file '{uri.AbsoluteUri}' does not match expected value."); Logger?.Error(exception, exception.Message); throw exception; } } else { Logger.Trace($"Skipping validation because validation context of Component {component.Name} is not valid."); } } } Logger?.Info($"Download of '{uri.AbsoluteUri}' succeeded using engine '{engine.Name}'"); PreferredDownloadEngines.Instance.LastSuccessfulEngineName = engine.Name; engineSummary.DownloadEngine = engine.Name; return(engineSummary); } catch (OperationCanceledException) { throw; } catch (Exception ex) { failureList.Add(new DownloadFailureInformation(ex, engine.Name)); Logger.Trace($"Download failed using {engine.Name} engine. {ex}"); if (engine.Equals(engines.LastOrDefault())) { throw new DownloadFailureException(failureList); } cancellationToken.ThrowIfCancellationRequested(); outputStream.SetLength(length); outputStream.Seek(position, SeekOrigin.Begin); var millisecondsTimeout = SleepDurationBetweenRetries; if (millisecondsTimeout < 0) { millisecondsTimeout = 0; } Logger.Trace($"Sleeping {millisecondsTimeout} before retrying download."); Thread.Sleep(millisecondsTimeout); } } return(null); }
/// <summary> /// Writes an array of FLOAT32, INT32, DOUBLE64 or ASCII (char) values to the device. /// The data length is given by the value parameter. /// This method does only return when all data is downloaded. /// The data is being written in an loop with several sub queries. /// During this command is working, it is possible to use other commands with an different thread. /// </summary> /// <param name="address">Device Address. Use null to use the DefaultDeviceAddress defined on MeComQuerySet.</param> /// <param name="parameterId">Device Parameter ID.</param> /// <param name="instance">Parameter Instance. (usually 1)</param> /// <param name="type">Specifies the type of the value to be written.</param> /// <param name="values">Data to be written (can be float[], int[], double[] or string.</param> /// <param name="callback">Is called every time when the progress has changed.</param> /// <exception cref="ComCommandException">when the command fails. Check the inner exception for details.</exception> public void SetBigData(byte?address, UInt16 parameterId, byte instance, MeParType type, dynamic values, ProgressUpdateCallback callback) { int maxDataSize = meQuerySet.MaxTxPayloadSize - 22; //-xx Bytes used for commands int nrOfElementsPerPackage = 0; switch (type) { case MeParType.FLOAT32: nrOfElementsPerPackage = maxDataSize / 8; break; case MeParType.INT32: nrOfElementsPerPackage = maxDataSize / 8; break; case MeParType.DOUBLE64: nrOfElementsPerPackage = maxDataSize / 16; break; case MeParType.LATIN1: values += (char)0; //Add zero terminator nrOfElementsPerPackage = maxDataSize / 2; break; case MeParType.BYTE: nrOfElementsPerPackage = maxDataSize / 2; break; default: throw new ArgumentOutOfRangeException("Unknown EParType: " + type); } int nrOfPackages = (values.Length - 1) / nrOfElementsPerPackage + 1; try { int totalSentElements = 0; MemoryStream totalStream = new MemoryStream(); for (int packageNr = 0; packageNr < nrOfPackages; packageNr++) { bool lastPackage = (packageNr + 1) == nrOfPackages; int nrOfElementsInThisPackage = values.Length - totalSentElements; if (nrOfElementsInThisPackage > nrOfElementsPerPackage) { nrOfElementsInThisPackage = nrOfElementsPerPackage; } MeComPacket txFrame = new MeComPacket('#', address); MeComVarConvert.AddString(txFrame.Payload, "VB"); MeComVarConvert.AddUint16(txFrame.Payload, parameterId); MeComVarConvert.AddUint8(txFrame.Payload, instance); MeComVarConvert.AddUint32(txFrame.Payload, (uint)totalSentElements); //write start position MeComVarConvert.AddUint16(txFrame.Payload, (ushort)nrOfElementsInThisPackage); if (lastPackage) { MeComVarConvert.AddUint8(txFrame.Payload, 1); } else { MeComVarConvert.AddUint8(txFrame.Payload, 0); } switch (type) { case MeParType.FLOAT32: for (int i = 0; i < nrOfElementsInThisPackage; i++) { MeComVarConvert.AddFloat32(txFrame.Payload, values[totalSentElements + i]); } break; case MeParType.INT32: for (int i = 0; i < nrOfElementsInThisPackage; i++) { MeComVarConvert.AddUint32(txFrame.Payload, values[totalSentElements + i]); } break; case MeParType.DOUBLE64: for (int i = 0; i < nrOfElementsInThisPackage; i++) { MeComVarConvert.AddDouble64(txFrame.Payload, values[totalSentElements + i]); } break; case MeParType.LATIN1: MeComVarConvert.AddEncodedString(txFrame.Payload, values.Substring(totalSentElements, nrOfElementsInThisPackage)); break; case MeParType.BYTE: for (int i = 0; i < nrOfElementsInThisPackage; i++) { MeComVarConvert.AddUint8(txFrame.Payload, values[totalSentElements + i]); } break; } int timeout = 0; while (timeout < 50) //Manage device busy { timeout++; try { meQuerySet.Set(txFrame); break; } catch (ServerException ex) { if (ex.ServerErrorCode != 2) { throw; } TraceLog.Verbose("Device busy detected. Timeout {0}", timeout); Thread.Sleep(10); } } totalSentElements += nrOfElementsInThisPackage; callback?.Invoke(100.0 / nrOfPackages * (packageNr + 1)); } } catch (Exception Ex) { throw new ComCommandException(String.Format("Set Value failed: Address: {0}; ID: {1}; Inst: {2}; Detail: {3}", address, parameterId, instance, Ex.Message), Ex); } }
/// <summary> /// Reads an array of FLOAT32, INT32, DOUBLE64 or ASCII (char) values form the device. /// The data length is given by the device. /// This method does only return when the device tells the host, that all data is read. /// The data is read in an loop with several sub queries. /// During this command is working, it is possible to use other commands with an different thread. /// </summary> /// <param name="address">Device Address. Use null to use the DefaultDeviceAddress defined on MeComQuerySet.</param> /// <param name="parameterId">Device Parameter ID.</param> /// <param name="instance">Parameter Instance. (usually 1)</param> /// <param name="type">Specifies the type of the value to be read.</param> /// <param name="callback">Is called every time when the progress has changed.</param> /// <param name="expectedNrOfElements">Defines the expected number of elements to calculate the progress for the callback function.</param> /// <returns>Returned value.</returns> /// <exception cref="ComCommandException">when the command fails. Check the inner exception for details.</exception> public dynamic GetBigData(byte?address, UInt16 parameterId, byte instance, MeParType type, ProgressUpdateCallback callback = null, int expectedNrOfElements = 0) { dynamic value; try { ushort rcvElements; bool hasMoreData; uint totalReadElements = 0; MemoryStream totalStream = new MemoryStream(); do { MeComPacket txFrame = new MeComPacket('#', address); MeComVarConvert.AddString(txFrame.Payload, "?VB"); MeComVarConvert.AddUint16(txFrame.Payload, parameterId); MeComVarConvert.AddUint8(txFrame.Payload, instance); MeComVarConvert.AddUint32(txFrame.Payload, totalReadElements); //Read start position MeComVarConvert.AddUint16(txFrame.Payload, UInt16.MaxValue); //Maximum Elements to read per call. MeComPacket rxFrame = meQuerySet.Query(txFrame); rcvElements = MeComVarConvert.ReadUint16(rxFrame.Payload); hasMoreData = MeComVarConvert.ReadUint8(rxFrame.Payload) == 1; totalReadElements += rcvElements; if (rcvElements > 0) { rxFrame.Payload.CopyTo(totalStream); } callback?.Invoke(100.0 / expectedNrOfElements * totalReadElements); } while (hasMoreData); totalStream.Position = 0; callback?.Invoke(100); switch (type) { case MeParType.FLOAT32: value = new float[totalReadElements]; break; case MeParType.INT32: value = new int[totalReadElements]; break; case MeParType.DOUBLE64: value = new double[totalReadElements]; break; case MeParType.LATIN1: value = ""; break; case MeParType.BYTE: value = new byte[totalReadElements]; break; default: throw new ArgumentOutOfRangeException("Unknown EParType: " + type); } for (int i = 0; i < totalReadElements; i++) { switch (type) { case MeParType.FLOAT32: value[i] = MeComVarConvert.ReadFloat32(totalStream); break; case MeParType.INT32: value[i] = MeComVarConvert.ReadInt32(totalStream); break; case MeParType.DOUBLE64: value[i] = MeComVarConvert.ReadDouble64(totalStream); break; case MeParType.LATIN1: value = MeComVarConvert.ReadEncodedString(totalStream, (int)totalReadElements); return(value); case MeParType.BYTE: value[i] = MeComVarConvert.ReadUint8(totalStream); break; default: throw new ArgumentOutOfRangeException("Unknown EParType: " + type); } } return(value); } catch (Exception Ex) { throw new ComCommandException(String.Format("Get Value failed: Address: {0}; ID: {1}; Inst: {2}; Detail: {3}", address, parameterId, instance, Ex.Message), Ex); } }
protected override DownloadSummary DownloadCore(Uri uri, Stream outputStream, ProgressUpdateCallback progress, CancellationToken cancellationToken, IComponent?component) { var summary = new DownloadSummary(); using var webResponse = GetWebResponse(uri, ref summary, out var webRequest, cancellationToken); if (webResponse != null) { var registration1 = cancellationToken.Register(() => webResponse.Close()); try { using var responseStream = webResponse.GetResponseStream(); var header = webResponse.Headers["Content-Length"]; if (string.IsNullOrEmpty(header)) { throw new IOException("Error: Content-Length is missing from response header."); } var totalStreamLength = (long)Convert.ToInt32(header); if (totalStreamLength.Equals(0L)) { throw new IOException("Error: Response stream length is 0."); } var streamReadError = false; var totalBytesRead = 0L; var array = new byte[Math.Max(1024L, Math.Min(totalStreamLength, 32768L))]; var registration2 = cancellationToken.Register(() => webRequest.Abort()); try { while (true) { cancellationToken.ThrowIfCancellationRequested(); var bytesRead = responseStream.Read(array, 0, array.Length); streamReadError = bytesRead < 0; if (bytesRead <= 0) { break; } totalBytesRead += bytesRead; outputStream.Write(array, 0, bytesRead); if (totalStreamLength < totalBytesRead) { totalStreamLength = totalBytesRead; } progress?.Invoke(new ProgressUpdateStatus(totalBytesRead, totalStreamLength, 0)); } } finally { registration2.Dispose(); } cancellationToken.ThrowIfCancellationRequested(); if (streamReadError) { throw new IOException("Internal error while downloading the stream."); } summary.DownloadedSize = totalBytesRead; return(summary); } catch (WebException ex) { var message = cancellationToken.IsCancellationRequested ? "DownloadCore failed along with a cancellation request." : "DownloadCore failed"; if (cancellationToken.IsCancellationRequested) { Logger.Trace("WebClient error '" + ex.Status + "' with '" + uri.AbsoluteUri + "' - " + message); cancellationToken.ThrowIfCancellationRequested(); } else { Logger.Trace("WebClient error '" + ex.Status + "' with '" + uri.AbsoluteUri + "'."); throw; } } finally { registration1.Dispose(); } } return(summary); }
public static List <uint> ByteArrayFindAll(byte[] InputBuffer, byte[] ToSearch, ProgressUpdateCallback ProgressCallback) { List <uint> Found = new List <uint>(); for (int i = 0; i < InputBuffer.Length - ToSearch.Length; i++) { if (ProgressCallback != null) { ProgressCallback((int)Math.Round((i / (double)InputBuffer.Length) * 100)); } if (BComp(InputBuffer, i, ToSearch)) { Found.Add((uint)i); } } return(Found); }
protected abstract DownloadSummary DownloadCore(Uri uri, Stream outputStream, ProgressUpdateCallback progress, CancellationToken cancellationToken, IComponent?component);
public DownloadSummary Download(Uri uri, Stream outputStream, ProgressUpdateCallback progress, CancellationToken cancellationToken, IComponent?component) { return(DownloadWithBitRate(uri, outputStream, progress, cancellationToken, component)); }
public static extern void RegisterProgressUpdateCallbackNative([MarshalAs(UnmanagedType.FunctionPtr)] ProgressUpdateCallback call);