diff --git a/USB Test App WPF/MainWindow.xaml b/USB Test App WPF/MainWindow.xaml
index 0246498..05fb1ff 100644
--- a/USB Test App WPF/MainWindow.xaml
+++ b/USB Test App WPF/MainWindow.xaml
@@ -27,30 +27,33 @@
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
diff --git a/USB Test App WPF/MainWindow.xaml.cs b/USB Test App WPF/MainWindow.xaml.cs
index 5226d31..ee743bb 100644
--- a/USB Test App WPF/MainWindow.xaml.cs
+++ b/USB Test App WPF/MainWindow.xaml.cs
@@ -1092,5 +1092,46 @@ private void DeployFileTestButton_Click(object sender, RoutedEventArgs e)
// enable button
(sender as Button).IsEnabled = true;
}
+
+ private void UploadFileInternalStorage_Click(object sender, RoutedEventArgs e)
+ {
+ string fileContent = "1. This is a test file to upload in internal storage. A long message to test more than just a line.\r\n" +
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
+ "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " +
+ "2. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\r\n" +
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
+ "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " +
+ "3. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\r\n" +
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
+ "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " +
+ "4. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\r\n";
+ //string fileContent = "simple test file";
+ string fileName = "I:\\upload.txt";
+
+ // disable button
+ (sender as Button).IsEnabled = false;
+
+ var reply1 = (DataContext as MainViewModel).AvailableDevices[DeviceGrid.SelectedIndex].DebugEngine.AddStorageFile(fileName, Encoding.UTF8.GetBytes(fileContent));
+ Debug.WriteLine($"File upload internal success: {reply1}");
+
+ // enable button
+ (sender as Button).IsEnabled = true;
+ }
+
+ private void RemoveFileInternalStorage_Click(object sender, RoutedEventArgs e)
+ {
+ string fileName = "I:\\upload.txt";
+ // disable button
+ (sender as Button).IsEnabled = false;
+
+ var reply1 = (DataContext as MainViewModel).AvailableDevices[DeviceGrid.SelectedIndex].DebugEngine.DeleteStorageFile(fileName);
+ Debug.WriteLine($"File upload internal success: {reply1}");
+
+ // enable button
+ (sender as Button).IsEnabled = true;
+ }
}
}
diff --git a/nanoFramework.Tools.DebugLibrary.Shared/WireProtocol/Commands.cs b/nanoFramework.Tools.DebugLibrary.Shared/WireProtocol/Commands.cs
index 870f07e..7a72455 100644
--- a/nanoFramework.Tools.DebugLibrary.Shared/WireProtocol/Commands.cs
+++ b/nanoFramework.Tools.DebugLibrary.Shared/WireProtocol/Commands.cs
@@ -41,6 +41,45 @@ public enum AccessMemoryErrorCodes : uint
Unknown = 0xFFFF,
}
+ ///
+ /// Storage operation error codes.
+ ///
+ public enum StorageOperationErrorCode : uint
+ {
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // NEED TO KEEP THESE IN SYNC WITH native 'StorageOperationErrorCode' enum in Debugger.h //
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// No error.
+ ///
+ NoError = 0x0001,
+
+ ///
+ /// Write error.
+ ///
+ WriteError = 0x0010,
+
+ ///
+ /// Delete error.
+ ///
+ DeleteError = 0x0020,
+
+ ///
+ /// Platform dependent error.
+ ///
+ PlatformError = 0x0030,
+
+ /////////////////////////////////////////////////////////
+ // The following element is not present in native code //
+ /////////////////////////////////////////////////////////
+
+ ///
+ /// Target does not support storage operations.
+ ///
+ NotSupported = 0xFFFF,
+ }
+
public class Commands
{
public const uint c_Monitor_Ping = 0x00000000; // The payload is empty, this command is used to let the other side know we are here...
@@ -58,6 +97,7 @@ public class Commands
public const uint c_Monitor_OemInfo = 0x0000000E;
public const uint c_Monitor_QueryConfiguration = 0x0000000F;
public const uint c_Monitor_UpdateConfiguration = 0x00000010;
+ public const uint c_Monitor_StorageOperation = 0x00000011;
public const uint c_Monitor_TargetInfo = 0x00000020;
public class Monitor_Message : IConverter
@@ -529,6 +569,136 @@ public override bool PrepareForSend(byte[] data, int length, int offset = 0)
}
}
+ ///
+ /// Perform storage operation on the target device.
+ ///
+ public class Monitor_StorageOperation : OverheadBase
+ {
+ ///
+ /// Storage operation to be performed.
+ ///
+ public uint Operation = (byte)StorageOperation.None;
+
+ ///
+ /// File name for the the storage operation.
+ ///
+ [IgnoreDataMember]
+ public string FileName = string.Empty;
+
+ ///
+ /// Length of the name of the file to be used in the operation.
+ ///
+ public uint NameLength = 0;
+
+ ///
+ /// Length of the data to be used in the operation.
+ ///
+ public uint DataLength = 0;
+
+ ///
+ /// Offset in the file data of the chunck in this operation.
+ ///
+ ///
+ /// This is to be used by the target device to know where to start writing the chunk data.
+ ///
+ public uint Offset = 0;
+
+ ///
+ /// Data buffer to be sent to the device.
+ ///
+ public byte[] Data;
+
+ public class Reply
+ {
+ public uint ErrorCode;
+ };
+
+ ///
+ /// Prepare for sending a storage operation to the target device.
+ ///
+ /// to be performed.
+ /// Name of the file to be used in the operation.
+ public void SetupOperation(
+ StorageOperation operation,
+ string name)
+ {
+ Operation = (uint)operation;
+ FileName = name;
+ }
+
+ ///
+ /// Prepare for sending a storage operation to the target device.
+ ///
+ /// Data buffer to be sent to the device.
+ /// Offset in the to start copying data from.
+ /// Length of the data to be copied from the .
+ public override bool PrepareForSend(
+ byte[] buffer,
+ int length,
+ int offset = 0)
+ {
+ // setup the data payload
+ DataLength = (uint)length;
+ Data = new byte[length + FileName.Length];
+
+ // add the file name to the data property buffer
+ var tempName = Encoding.UTF8.GetBytes(FileName);
+ NameLength = (uint)tempName.Length;
+ Array.Copy(tempName, 0, Data, 0, NameLength);
+
+ // copy the buffer data to the data property buffer
+ Array.Copy(buffer, offset, Data, NameLength, length);
+
+ return true;
+ }
+
+ internal void PrepareForSend()
+ {
+ // add the file name to the data property buffer
+ Data = Encoding.UTF8.GetBytes(FileName);
+ NameLength = (uint)FileName.Length;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // !!! KEEP IN SYNC WITH typedef enum Monitor_StorageOperation (in native code) !!! //
+ //////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Storage operation to be performed.
+ ///
+ public enum StorageOperation : byte
+ {
+ ///
+ /// Not specified.
+ ///
+ None = 0,
+
+ ///
+ /// Write to storage.
+ ///
+ ///
+ /// If the file already exists, it will be overwritten.
+ ///
+ Write = 1,
+
+ ///
+ /// Delete from storage.
+ ///
+ ///
+ /// If the file doesn't exist, no action is taken.
+ ///
+ Delete = 2,
+
+ ///
+ /// Append to a file.
+ ///
+ ///
+ /// If the file doesn't exist, no action is taken.
+ ///
+ Append = 3
+ }
+ }
+
public const uint c_Debugging_Execution_BasePtr = 0x00020000; // Returns the pointer for the ExecutionEngine object.
public const uint c_Debugging_Execution_ChangeConditions = 0x00020001; // Sets/resets the state of the debugger.
public const uint c_Debugging_Execution_SecurityKey = 0x00020002; // Sets security key.
@@ -1857,7 +2027,7 @@ internal static string GetZeroTerminatedString(byte[] buf, bool fUTF8)
while (len < num && buf[len] != 0) len++;
if (fUTF8) return Encoding.UTF8.GetString(buf, 0, len);
- else return Encoding.UTF8.GetString(buf, 0, len);
+ else return Encoding.ASCII.GetString(buf, 0, len);
}
public static object ResolveCommandToPayload(uint cmd, bool fReply, CLRCapabilities capabilities)
@@ -1878,6 +2048,7 @@ public static object ResolveCommandToPayload(uint cmd, bool fReply, CLRCapabilit
case c_Monitor_FlashSectorMap: return new Monitor_FlashSectorMap.Reply();
case c_Monitor_QueryConfiguration: return new Monitor_QueryConfiguration.Reply();
case c_Monitor_UpdateConfiguration: return new Monitor_UpdateConfiguration.Reply();
+ case c_Monitor_StorageOperation: return new Monitor_StorageOperation.Reply();
case c_Debugging_Execution_BasePtr: return new Debugging_Execution_BasePtr.Reply();
case c_Debugging_Execution_ChangeConditions: return new DebuggingExecutionChangeConditions.Reply();
@@ -1951,6 +2122,7 @@ public static object ResolveCommandToPayload(uint cmd, bool fReply, CLRCapabilit
case c_Monitor_DeploymentMap: return new Monitor_DeploymentMap();
case c_Monitor_FlashSectorMap: return new Monitor_FlashSectorMap();
case c_Monitor_QueryConfiguration: return new Monitor_QueryConfiguration();
+ case c_Monitor_StorageOperation: return new Monitor_StorageOperation();
case c_Debugging_Execution_BasePtr: return new Debugging_Execution_BasePtr();
case c_Debugging_Execution_ChangeConditions: return new DebuggingExecutionChangeConditions();
diff --git a/nanoFramework.Tools.DebugLibrary.Shared/WireProtocol/Engine.cs b/nanoFramework.Tools.DebugLibrary.Shared/WireProtocol/Engine.cs
index 59e11fd..858deae 100644
--- a/nanoFramework.Tools.DebugLibrary.Shared/WireProtocol/Engine.cs
+++ b/nanoFramework.Tools.DebugLibrary.Shared/WireProtocol/Engine.cs
@@ -32,6 +32,11 @@ public partial class Engine : IDisposable, IControllerHostLocal
///
private const int _InterRequestSleep = 2;
+ ///
+ /// This constant is to be used in progress report when performing delete operation.
+ ///
+ public const int StorageDeleteOperationProgressStep = 10;
+
internal IPort _portDefinition;
internal Controller _controlller { get; set; }
@@ -4423,6 +4428,224 @@ public int GetPacketMaxLength(Commands.OverheadBase cmd)
return (int)WireProtocolPacketSize - cmd.Overhead;
}
+ #region Storage methods
+
+ ///
+ /// Add a file to the device storage.
+ ///
+ /// File name.
+ /// Content of the file.
+ /// Count of bytes already executed in this operation.
+ /// Total length of the operation, in bytes.
+ /// An object to track the progress of the deploy operation.
+ /// An object to log the progress of the deploy operation.
+ /// Operation result as a .
+ public StorageOperationErrorCode AddStorageFile(
+ string fileName,
+ byte[] fileContent,
+ int operationExecutedLenght = 0,
+ int operationTotalLength = 0,
+ IProgress progress = null,
+ IProgress log = null)
+ {
+ // counters to manage the chunked update process
+ int count = fileContent.Length;
+ int position = 0;
+ int packetLength = 0;
+
+ StorageOperationErrorCode operationOutcome = StorageOperationErrorCode.WriteError;
+
+ log?.Report($"Storing '{fileName}'...");
+
+ while (count > 0)
+ {
+ // Send by chunk, the first chunk is a write, the others will be appened
+ var storageOperation = new Commands.Monitor_StorageOperation();
+
+ storageOperation.SetupOperation(
+ position == 0 ? Commands.Monitor_StorageOperation.StorageOperation.Write : Commands.Monitor_StorageOperation.StorageOperation.Append,
+ fileName);
+
+ // get packet length, either the maximum allowed size or whatever is still available to TX
+ packetLength = Math.Min(GetPacketMaxLength(storageOperation), count);
+
+ storageOperation.PrepareForSend(fileContent, packetLength, position);
+
+ // update offset field
+ storageOperation.Offset = (uint)position;
+
+ progress?.Report(new MessageWithProgress($"Storing '{fileName}' file in target storage...", (uint)(operationExecutedLenght + position + packetLength), (uint)operationTotalLength));
+ log?.Report($"Sent {position + packetLength}/{fileContent.Length} bytes.");
+
+ // send the file chunk to the device
+ IncomingMessage reply = PerformSyncRequest(Commands.c_Monitor_StorageOperation, 0, storageOperation);
+
+ if (reply != null)
+ {
+ if (!reply.IsPositiveAcknowledge())
+ {
+ // target doesn't support this command
+ operationOutcome = StorageOperationErrorCode.NotSupported;
+ }
+ else
+ {
+ if (reply.Payload is Commands.Monitor_StorageOperation.Reply cmdReply)
+ {
+ operationOutcome = (StorageOperationErrorCode)cmdReply.ErrorCode;
+ if (operationOutcome != StorageOperationErrorCode.NoError)
+ {
+ // done here
+ break;
+ }
+ }
+ else
+ {
+ // set generic write error
+ operationOutcome = StorageOperationErrorCode.WriteError;
+
+ // done here
+ break;
+ }
+ }
+ }
+ else
+ {
+ // set generic write error
+ operationOutcome = StorageOperationErrorCode.WriteError;
+
+ // done here
+ break;
+ }
+
+ // update counters
+ count -= packetLength;
+ position += packetLength;
+ }
+
+ // clear progress report message
+ progress?.Report(new MessageWithProgress(""));
+
+ if (operationOutcome != StorageOperationErrorCode.NoError)
+ {
+ // there was an error, compose error message...
+ var errorMessage = new StringBuilder($"Error sending {packetLength} bytes to device. Store operation failed. ");
+
+ switch (operationOutcome)
+ {
+ case StorageOperationErrorCode.PlatformError:
+ errorMessage.Append("Error is platform related.");
+ break;
+
+ case StorageOperationErrorCode.NotSupported:
+ errorMessage = new StringBuilder($"Target doesn't have support to access storage. "); ;
+ break;
+
+ default:
+ errorMessage.Append("Unknown error.");
+ break;
+ }
+
+ // ...and report it
+ log?.Report(errorMessage.ToString());
+ }
+
+ return operationOutcome;
+ }
+
+ ///
+ /// Delete a file from the device storage.
+ ///
+ /// Name of file to delete.
+ /// Count of bytes already executed in this operation.
+ /// Total length of the operation, in bytes.
+ /// An object to track the progress of the deploy operation.
+ /// An object to log the progress of the deploy operation.
+ /// Operation result as a .
+ ///
+ /// When computing the operation total length, use the constant as the step weight and consider that this will be executed in a single step.
+ ///
+ public StorageOperationErrorCode DeleteStorageFile(
+ string fileName,
+ int operationExecutedLenght = 0,
+ int operationTotalLength = 0,
+ IProgress progress = null,
+ IProgress log = null)
+ {
+ StorageOperationErrorCode operationOutcome = StorageOperationErrorCode.DeleteError;
+
+ var storop = new Commands.Monitor_StorageOperation();
+
+ storop.SetupOperation(
+ Commands.Monitor_StorageOperation.StorageOperation.Delete,
+ fileName);
+
+ storop.PrepareForSend();
+
+ progress?.Report(new MessageWithProgress($"Deleting '{fileName}' from target storage...", (uint)(operationExecutedLenght + StorageDeleteOperationProgressStep), (uint)operationTotalLength));
+ log?.Report($"Deleting '{fileName}' from target storage...");
+
+ IncomingMessage reply = PerformSyncRequest(
+ Commands.c_Monitor_StorageOperation,
+ 0,
+ storop);
+
+ if (reply != null)
+ {
+ if (!reply.IsPositiveAcknowledge())
+ {
+ // target doesn't support this command
+ operationOutcome = StorageOperationErrorCode.NotSupported;
+ }
+ else
+ {
+ if (reply.Payload is Commands.Monitor_StorageOperation.Reply cmdReply)
+ {
+ operationOutcome = (StorageOperationErrorCode)cmdReply.ErrorCode;
+ }
+ else
+ {
+ // set generic write error
+ operationOutcome = StorageOperationErrorCode.DeleteError;
+ }
+ }
+ }
+
+ // clear progress report message
+ progress?.Report(new MessageWithProgress(""));
+
+ if (operationOutcome != StorageOperationErrorCode.NoError)
+ {
+ // there was an error, compose error message...
+ var errorMessage = new StringBuilder($"Error deleting file from storage. ");
+
+ switch (operationOutcome)
+ {
+ case StorageOperationErrorCode.PlatformError:
+ errorMessage.Append("Error platform related.");
+ break;
+
+ case StorageOperationErrorCode.DeleteError:
+ errorMessage.Append("Couldn't delete the file.");
+ break;
+
+ case StorageOperationErrorCode.NotSupported:
+ errorMessage = new StringBuilder($"Target doesn't have support to access storage. "); ;
+ break;
+
+ default:
+ errorMessage.Append("Unknown error.");
+ break;
+ }
+
+ // ...and report it
+ log?.Report(errorMessage.ToString());
+ }
+
+ return operationOutcome;
+ }
+
+ #endregion
+
///
/// Writes a specific configuration block to the device.
/// The configuration block is updated only with the changes for this configuration part.