Skip to content

Commit b057fbd

Browse files
authored
Adding File Deployment to nanoff (#261)
1 parent e3ff02e commit b057fbd

File tree

7 files changed

+379
-8
lines changed

7 files changed

+379
-8
lines changed

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,50 @@ nanoff --listtargets --platform stm32
447447

448448
If you use the `--listtargets` switch in conjunction with `--preview`, you'll get the list of available firmware packages that are available with experimental or major feature changes.
449449

450+
## Deploy file to device storage
451+
452+
Some devices like ESP32, Orgpal and few others have storage available. Files can be deployed in this storage. You have to use the `filedeployment` parameter pointing on a JSON file to deploy files while flashing the device:
453+
454+
```console
455+
nanoff --target XIAO_ESP32C3 --update --masserase --serialport COM21 --filedeployment C:\path\deploy.json
456+
```
457+
458+
The JSON an optional `SerialPort` in case the port to upload the files must be different than the one to flash the device or not specified in the main command line and a **mandatory** list of `Files` entries. Each entry must contains `DestinationFilePath`, the destination full path file name and `SourceFilePath` to deploy content, otherwise to delete the file, the full path with file name of the source file to be deployed:
459+
460+
```json
461+
{
462+
"serialport":"COM42",
463+
"files": [
464+
{
465+
"DestinationFilePath": "I:\\TestFile.txt",
466+
"SourceFilePath": "C:\\tmp\\NFApp3\\NFApp3\\TestFile.txt"
467+
},
468+
{
469+
"DestinationFilePath": "I:\\NoneFile.txt"
470+
},
471+
{
472+
"DestinationFilePath": "I:\\wilnotexist.txt",
473+
"SourceFilePath": "C:\\WRONGPATH\\TestFile.txt"
474+
}
475+
]
476+
}
477+
```
478+
479+
In the case you just want to deploy the files without any other operation, you can just specify:
480+
481+
```console
482+
nanoff --filedeployment C:\path\deploy.json
483+
```
484+
485+
In that case, the `SerialPort` must be present in the JSON file.
486+
487+
> [!Note]
488+
> If a file already exists in the storage, it will be replaced by the new one.
489+
>
490+
> If a file does not exist and is requested to be deleted, nothing will happen, a warning will be displayed.
491+
>
492+
> If a file can't be uploaded because of a problem, the deployment of the other files will continue and an error will be displayed.
493+
450494
## Clear cache location
451495

452496
If needed one can clear the local cache from the firmware packages that are stored there.

nanoFirmwareFlasher.Library/ExitCodes.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ public enum ExitCodes
8181
[Display(Name = "Error executing operation with nano device.")]
8282
E2002 = 2002,
8383

84+
/// <summary>
85+
/// Error executing operation with nano device.
86+
/// </summary>
87+
[Display(Name = "Error executing file deployment on nano device.")]
88+
E2003 = 2003,
89+
8490
////////////////////////
8591
// ESP32 tools Errors //
8692
////////////////////////
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace nanoFramework.Tools.FirmwareFlasher.FileDeployment
8+
{
9+
internal class DeploymentFile
10+
{
11+
/// <summary>
12+
/// Gets or sets the destination fully qualified file path including file name.
13+
/// </summary>
14+
public string DestinationFilePath { get; set; }
15+
16+
/// <summary>
17+
/// Gets or sets the source fully qualified file path including file name.
18+
/// </summary>
19+
public string SourceFilePath { get; set; }
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace nanoFramework.Tools.FirmwareFlasher.FileDeployment
8+
{
9+
internal class FileDeploymentConfiguration
10+
{
11+
/// <summary>
12+
/// Gets or sets the serial port to be used for the deployment.
13+
/// </summary>
14+
public string SerialPort { get; set; }
15+
16+
/// <summary>
17+
/// A list of files to deploy and/or delete.
18+
/// </summary>
19+
public List<DeploymentFile> Files { get; set; }
20+
}
21+
}
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
using nanoFramework.Tools.Debugger;
2+
using nanoFramework.Tools.Debugger.Extensions;
3+
using Newtonsoft.Json;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Runtime;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
12+
namespace nanoFramework.Tools.FirmwareFlasher.FileDeployment
13+
{
14+
/// <summary>
15+
/// File Deployment Manager class.
16+
/// </summary>
17+
public class FileDeploymentManager
18+
{
19+
private FileDeploymentConfiguration _configuration;
20+
private VerbosityLevel _verbosity;
21+
private string _serialPort;
22+
23+
/// <summary>
24+
/// Creates an instance of FileDeploymentManager.
25+
/// </summary>
26+
public FileDeploymentManager(string configFilePath, string originalPort, VerbosityLevel verbosity)
27+
{
28+
_configuration = JsonConvert.DeserializeObject<FileDeploymentConfiguration>(File.ReadAllText(configFilePath));
29+
_serialPort = string.IsNullOrEmpty(_configuration.SerialPort) ? originalPort : _configuration.SerialPort;
30+
_verbosity = verbosity;
31+
}
32+
33+
/// <summary>
34+
/// Deploys async the files.
35+
/// </summary>
36+
/// <returns>An ExitCode error.</returns>
37+
public async Task<ExitCodes> DeployAsync()
38+
{
39+
// number of retries when performing a deploy operation
40+
const int _numberOfRetries = 5;
41+
// timeout when performing a deploy operation
42+
const int _timeoutMiliseconds = 1000;
43+
44+
NanoDeviceBase device = null;
45+
PortBase serialDebugClient;
46+
int retryCount = 0;
47+
48+
serialDebugClient = PortBase.CreateInstanceForSerial(false);
49+
50+
try
51+
{
52+
serialDebugClient.AddDevice(_serialPort);
53+
54+
device = serialDebugClient.NanoFrameworkDevices[0];
55+
}
56+
catch (Exception ex)
57+
{
58+
Console.ForegroundColor = ConsoleColor.Red;
59+
Console.WriteLine($"Error connecting to nanoDevice on {_serialPort} to deploy files");
60+
Console.ForegroundColor = ConsoleColor.White;
61+
return ExitCodes.E2000;
62+
}
63+
64+
// check if debugger engine exists
65+
if (device.DebugEngine == null)
66+
{
67+
device.CreateDebugEngine();
68+
if (_verbosity >= VerbosityLevel.Normal)
69+
{
70+
Console.WriteLine($"Debug engine created.");
71+
}
72+
}
73+
74+
bool deviceIsInInitializeState = false;
75+
76+
retryDebug:
77+
bool connectResult = device.DebugEngine.Connect(5000, true, true);
78+
if (retryCount == 0 && _verbosity >= VerbosityLevel.Normal)
79+
{
80+
Console.WriteLine($"Device connected and ready for file deployment.");
81+
}
82+
else if (_verbosity >= VerbosityLevel.Normal)
83+
{
84+
Console.WriteLine($"Device connect result is {connectResult}. Attempt {retryCount}/{_numberOfRetries}");
85+
}
86+
87+
if (!connectResult)
88+
{
89+
if (retryCount < _numberOfRetries)
90+
{
91+
// Give it a bit of time
92+
await Task.Delay(100);
93+
retryCount++;
94+
95+
goto retryDebug;
96+
}
97+
else
98+
{
99+
Console.ForegroundColor = ConsoleColor.Red;
100+
Console.WriteLine($"Error connecting to debug engine on nanoDevice on {_serialPort} to deploy files");
101+
Console.ForegroundColor = ConsoleColor.White;
102+
return ExitCodes.E2000;
103+
}
104+
}
105+
106+
retryCount = 0;
107+
108+
// initial check
109+
if (device.DebugEngine.IsDeviceInInitializeState())
110+
{
111+
if (_verbosity >= VerbosityLevel.Normal)
112+
{
113+
Console.ForegroundColor = ConsoleColor.Yellow;
114+
Console.WriteLine($"Device status verified as being in initialized state. Requesting to resume execution. Attempt {retryCount}/{_numberOfRetries}.");
115+
Console.ForegroundColor = ConsoleColor.White;
116+
}
117+
118+
// set flag
119+
deviceIsInInitializeState = true;
120+
121+
// device is still in initialization state, try resume execution
122+
device.DebugEngine.ResumeExecution();
123+
}
124+
125+
// handle the workflow required to try resuming the execution on the device
126+
// only required if device is not already there
127+
// retry 5 times with a 500ms interval between retries
128+
while (retryCount++ < _numberOfRetries && deviceIsInInitializeState)
129+
{
130+
if (!device.DebugEngine.IsDeviceInInitializeState())
131+
{
132+
if (_verbosity >= VerbosityLevel.Diagnostic)
133+
{
134+
Console.WriteLine($"Device has completed initialization.");
135+
}
136+
137+
// done here
138+
deviceIsInInitializeState = false;
139+
break;
140+
}
141+
142+
if (_verbosity >= VerbosityLevel.Diagnostic)
143+
{
144+
Console.WriteLine($"Waiting for device to report initialization completed ({retryCount}/{_numberOfRetries}).");
145+
}
146+
147+
// provide feedback to user on the 1st pass
148+
if (retryCount == 0)
149+
{
150+
if (_verbosity >= VerbosityLevel.Diagnostic)
151+
{
152+
Console.WriteLine($"Waiting for device to initialize.");
153+
}
154+
}
155+
156+
if (device.DebugEngine.IsConnectedTonanoBooter)
157+
{
158+
if (_verbosity >= VerbosityLevel.Diagnostic)
159+
{
160+
Console.WriteLine($"Device reported running nanoBooter. Requesting to load nanoCLR.");
161+
}
162+
163+
// request nanoBooter to load CLR
164+
device.DebugEngine.ExecuteMemory(0);
165+
}
166+
else if (device.DebugEngine.IsConnectedTonanoCLR)
167+
{
168+
if (_verbosity >= VerbosityLevel.Normal)
169+
{
170+
Console.ForegroundColor = ConsoleColor.Yellow;
171+
Console.WriteLine($"Device reported running nanoCLR. Requesting to reboot nanoCLR.");
172+
Console.ForegroundColor = ConsoleColor.White;
173+
}
174+
175+
await Task.Run(delegate
176+
{
177+
// already running nanoCLR try rebooting the CLR
178+
device.DebugEngine.RebootDevice(RebootOptions.ClrOnly);
179+
});
180+
}
181+
182+
// wait before next pass
183+
// use a back-off strategy of increasing the wait time to accommodate slower or less responsive targets (such as networked ones)
184+
await Task.Delay(TimeSpan.FromMilliseconds(_timeoutMiliseconds * (retryCount + 1)));
185+
186+
await Task.Yield();
187+
}
188+
189+
// check if device is still in initialized state
190+
if (!deviceIsInInitializeState)
191+
{
192+
// Deploy each file
193+
foreach (var file in _configuration.Files)
194+
{
195+
try
196+
{
197+
if (string.IsNullOrEmpty(file.SourceFilePath))
198+
{
199+
// deleting
200+
Console.Write($"Deleting file {file.DestinationFilePath}...");
201+
if (device.DebugEngine.DeleteStorageFile(file.DestinationFilePath) != Debugger.WireProtocol.StorageOperationErrorCode.NoError)
202+
{
203+
Console.ForegroundColor = ConsoleColor.Yellow;
204+
Console.WriteLine();
205+
Console.WriteLine($"Error deleting file {file.DestinationFilePath}, it may not exist on the storage.");
206+
Console.ForegroundColor = ConsoleColor.White;
207+
}
208+
else
209+
{
210+
Console.ForegroundColor = ConsoleColor.Green;
211+
Console.WriteLine($"OK");
212+
Console.ForegroundColor = ConsoleColor.White;
213+
}
214+
}
215+
else
216+
{
217+
Console.Write($"Deploying file {file.SourceFilePath} to {file.DestinationFilePath}...");
218+
var ret = device.DebugEngine.AddStorageFile(file.DestinationFilePath, File.ReadAllBytes(file.SourceFilePath));
219+
if (ret != Debugger.WireProtocol.StorageOperationErrorCode.NoError)
220+
{
221+
Console.ForegroundColor = ConsoleColor.Red;
222+
Console.WriteLine();
223+
Console.WriteLine($"Error deploying content file {file.SourceFilePath} to {file.DestinationFilePath}");
224+
Console.ForegroundColor = ConsoleColor.White;
225+
}
226+
else
227+
{
228+
Console.ForegroundColor = ConsoleColor.Green;
229+
Console.WriteLine($"OK");
230+
Console.ForegroundColor = ConsoleColor.White;
231+
}
232+
}
233+
}
234+
catch
235+
{
236+
if (_verbosity >= VerbosityLevel.Normal)
237+
{
238+
Console.ForegroundColor = ConsoleColor.Red;
239+
Console.WriteLine();
240+
Console.WriteLine($"Exception deploying content file {file.SourceFilePath} to {file.DestinationFilePath}");
241+
Console.ForegroundColor = ConsoleColor.White;
242+
}
243+
}
244+
}
245+
}
246+
else
247+
{
248+
return ExitCodes.E2002;
249+
}
250+
251+
return ExitCodes.OK;
252+
}
253+
}
254+
}

nanoFirmwareFlasher.Tool/Options.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,13 @@ public class Options
334334
HelpText = "Reads details from connected device.")]
335335
public bool DeviceDetails { get; set; }
336336

337+
[Option(
338+
"filedeployment",
339+
Required = false,
340+
Default = null,
341+
HelpText = "JSON file containing file deployment settings.")]
342+
public string FileDeployment { get; set; }
343+
337344
#endregion
338345

339346

0 commit comments

Comments
 (0)