diff --git a/README.md b/README.md
index 339adbb0..cdcd322a 100644
--- a/README.md
+++ b/README.md
@@ -503,6 +503,245 @@ In that case, the `SerialPort` must be present in the JSON file.
>
> 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.
+## Deploy Wireless, Wireless Access Point, Ethernet configuration and Certificates
+
+You can upload Wireless, Wireless Access Point, Ethernet configurations and Certificates during the flash operation so that your device is ready to go and those elements do not need to be stored in the code or beforehand in the internal storage. Depending on your device, some options may not be available. So check out what is available on your device before trying to upload them. The `--networkdeployment` can be combined with any other option.
+
+```console
+nanoff --networkdeployment C:\path\deploy.json
+```
+
+The json file can contains various configurations and thet are all optional:
+
+```json
+{
+ "serialport":"COM42",
+ "WirelessClient": { },
+ "WirelessAccessPoint": { },
+ "Ethernet": { },
+ // Only one or the other can be used
+ "DeviceCertificates": "base64",
+ "DeviceCertificatesPath": "c:\\pathto\\cert.pem",
+ // Only one or the other can be used
+ "CACertificates": "base64",
+ "CACertificatesPath": "c:\\pathto\\certca.pem"
+}
+```
+
+The optional `SerialPort` can be used in case the port to upload the configurations is different than the one to flash the device or not specified in the main command line like in the previous example.
+
+Here is a minimal example setting up a Wireless Client and a Wireless Access Point configuration at the same time:
+
+```json
+{"SerialPort":"COM10","WirelessClient":{"SSID":"MySSID","Password":"the_secret_password"},"WirelessAccessPoint":{"SSID":"nanoDevice","Password":null,"IPv4Address":"192.168.10.1","IPv4NetMAsk":"255.255.255.0","Authentication":"None"}}
+```
+
+See the section further to understand what are the mandatory fields and which ones are optionals.
+
+### Wireless Client options
+
+The `WirelessClient` object represents the wireless configuration settings for a network deployment. It contains the following properties:
+
+- **Ssid**:
+ - Type: `string`
+ - Format: 32 characters maximum.
+ - Mandatory
+
+- **Password**:
+ - Type: `string`
+ - Format: 64 characters maximum
+ - Default: empty string meaning no password.
+ - Optional
+
+- **Authentication**
+ - Type: `string`
+ - Possible values (case insensitive): `EAP, PEAP, WCN, OPEN, SHARED, WEP, WPA, WPA2, NONE`
+ - Description: the authentication type.
+ - Default: if nothing is specified, the internal value is not going to be changed
+
+- **Encryption**
+ - Type `string`
+ - Possible values (case insensitive): `WEP, WPA, WPA2, WPA_PSK, WPA2_PSK2, Certificate, None`
+ - Description: the encryption type.
+ - Default: if nothing is specified, the internal value is not going to be changed
+
+- **ConfigurationOption**
+ - Type: `string`
+ - Possible values (case insensitive): `None, Disable, Enable, AutoConnect, SmartConfig`
+ - Description: the configuration option.
+ - Default: if nothing is specified, the internal value is not going to be changed
+
+- **RadioType**
+ - Type: `string`
+ - Possible values (case insensitive): `802.11a, 802.11b, 802.11g, 802.11n`
+ - Description: the radio type.
+ - Default: if nothing is specified, the internal value is not going to be changed
+
+- **DhcpEnabled**:
+ - Type: `bool`
+ - Default: true
+ - Optional
+ - Description: a value indicating whether DHCP is enabled. If set to `false`, the `IPv4Address` and `IPv4NetMask` need to be set up.
+
+- **AutomaticDNS**:
+ - Type: `bool`
+ - Default: true
+ - Optional
+ - Description: a value indicating whether automatic DNS is enabled. If set to `false`, `Ipv4DNSAddress1` needs at least to be set.
+
+- **IPv4Address**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 address. This needs to be set if `DhcpEnabled` is `false`.
+
+- **IPv4NetMask**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 netmask. This needs to be set if `DhcpEnabled` is `false`.
+
+- **IPv4Gateway**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 gateway.
+
+- **IPv4DNSAddress1**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the primary IPv4 DNS address.
+
+- **IPv4DNSAddress2**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the secondary IPv4 DNS address.
+
+- **MacAddress**:
+ - Type: `string`
+ - Format: `AABBCCDDEEFF` or `AA:BB:CC:DD:EE:FF`
+ - Description: the MAC address.
+ - Note: some devivces do not allow to be set up, please check your device first.
+
+### Wireless Access Point options
+
+- **Ssid**:
+ - Type: `string`
+ - Format: 32 characters maximum.
+ - Mandatory
+
+- **Password**:
+ - Type: `string`
+ - Format: 64 characters maximum
+ - Default: empty string meaning no password.
+ - Optional
+
+- **Authentication**
+ - Type: `string`
+ - Possible values (case insensitive): `EAP, PEAP, WCN, OPEN, SHARED, WEP, WPA, WPA2, NONE`
+ - Description: the authentication type.
+ - Default: if nothing is specified, the internal value is not going to be changed
+
+- **Encryption**
+ - Type `string`
+ - Possible values (case insensitive): `WEP, WPA, WPA2, WPA_PSK, WPA2_PSK2, Certificate, None`
+ - Description: the encryption type.
+ - Default: if nothing is specified, the internal value is not going to be changed
+
+- **ConfigurationOption**
+ - Type: `string`
+ - Possible values (case insensitive): `None, Disable, Enable, AutoConnect, SmartConfig`
+ - Description: the configuration option.
+ - Default: if nothing is specified, the internal value is not going to be changed
+
+- **RadioType**
+ - Type: `string`
+ - Possible values (case insensitive): `802.11a, 802.11b, 802.11g, 802.11n`
+ - Description: the radio type.
+ - Default: if nothing is specified, the internal value is not going to be changed
+
+- **IPv4Address**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 address.
+ - Mandatory.
+
+- **IPv4NetMask**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 netmask.
+ - Mandatory.
+
+- **IPv4Gateway**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 gateway.
+ - Default: the `IPv4Address` value
+
+- **IPv4DNSAddress1**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the primary IPv4 DNS address.
+
+- **IPv4DNSAddress2**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the secondary IPv4 DNS address.
+
+- **MacAddress**:
+ - Type: `string`
+ - Format: `AABBCCDDEEFF` or `AA:BB:CC:DD:EE:FF`
+ - Description: the MAC address.
+ - Note: some devivces do not allow to be set up, please check your device first.
+
+### Ethernet options
+
+Represents an Ethernet configuration and here are the properties:
+
+- **DhcpEnabled**:
+ - Type: `bool`
+ - Default: true
+ - Optional
+ - Description: a value indicating whether DHCP is enabled. If set to `false`, the `IPv4Address` and `IPv4NetMask` need to be set up.
+
+- **AutomaticDNS**:
+ - Type: `bool`
+ - Default: true
+ - Optional
+ - Description: a value indicating whether automatic DNS is enabled. If set to `false`, `Ipv4DNSAddress1` needs at least to be set.
+
+- **IPv4Address**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 address. This needs to be set if `DhcpEnabled` is `false`.
+
+- **IPv4NetMask**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 netmask. This needs to be set if `DhcpEnabled` is `false`.
+
+- **IPv4Gateway**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the IPv4 gateway.
+
+- **IPv4DNSAddress1**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the primary IPv4 DNS address.
+
+- **IPv4DNSAddress2**:
+ - Type: `string`
+ - Format: `1.2.3.4` where 1, 2, 3 and 4 are bytes with values from 0 to 255.
+ - Description: the secondary IPv4 DNS address.
+
+- **MacAddress**:
+ - Type: `string`
+ - Format: `AABBCCDDEEFF` or `AA:BB:CC:DD:EE:FF`
+ - Description: the MAC address.
+ - Note: some devivces do not allow to be set up, please check your device first.
+
+### Device and CA Certificates
+
+You can either **base64** encode your certificates (`DeviceCertificates` and `CACertificates`) or provide a path on a certificate file (`DeviceCertificatesPath` and `CACertificatesPath`). Note that the certificate file can contain multiple certificates one after the other. This is especially usefull for CA certificates.
+
## Clear cache location
If needed one can clear the local cache from the firmware packages that are stored there.
diff --git a/nanoFirmwareFlasher.Library/DeploymentHelpers/DeviceHelper.cs b/nanoFirmwareFlasher.Library/DeploymentHelpers/DeviceHelper.cs
new file mode 100644
index 00000000..4edc973c
--- /dev/null
+++ b/nanoFirmwareFlasher.Library/DeploymentHelpers/DeviceHelper.cs
@@ -0,0 +1,181 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using nanoFramework.Tools.Debugger;
+using nanoFramework.Tools.Debugger.Extensions;
+
+namespace nanoFramework.Tools.FirmwareFlasher.DeploymentHelpers
+{
+ ///
+ /// Helper class to manage device operations.
+ ///
+ internal static class DeviceHelper
+ {
+ ///
+ /// Connects to a nanoDevice.
+ ///
+ /// The serial port.
+ /// Verbositiy level.
+ /// A tupple with the and the and the initialization state.
+ public static async Task<(NanoDeviceBase device, ExitCodes exitCode, bool deviceIsInInitializeState)> ConnectDevice(string serialPort, VerbosityLevel verbosity)
+ {
+ // number of retries when performing a deploy operation
+ const int _numberOfRetries = 5;
+ // timeout when performing a deploy operation
+ const int _timeoutMiliseconds = 1000;
+
+ NanoDeviceBase device = null;
+ PortBase serialDebugClient;
+ int retryCount = 0;
+
+ serialDebugClient = PortBase.CreateInstanceForSerial(false);
+
+ try
+ {
+ serialDebugClient.AddDevice(serialPort);
+
+ device = serialDebugClient.NanoFrameworkDevices[0];
+ }
+ catch (Exception)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Red;
+ OutputWriter.WriteLine($"Error connecting to nanoDevice on {serialPort} for deployment.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return (device, ExitCodes.E2000, true);
+ }
+
+ // check if debugger engine exists
+ if (device.DebugEngine == null)
+ {
+ device.CreateDebugEngine();
+ if (verbosity >= VerbosityLevel.Normal)
+ {
+ OutputWriter.WriteLine($"Debug engine created.");
+ }
+ }
+
+ bool deviceIsInInitializeState = false;
+
+ retryDebug:
+ bool connectResult = device.DebugEngine.Connect(5000, true, true);
+ if (retryCount == 0 && verbosity >= VerbosityLevel.Normal)
+ {
+ OutputWriter.WriteLine($"Device connected and ready for deployment.");
+ }
+ else if (verbosity >= VerbosityLevel.Normal)
+ {
+ OutputWriter.WriteLine($"Device connect result is {connectResult}. Attempt {retryCount}/{_numberOfRetries}");
+ }
+
+ if (!connectResult)
+ {
+ if (retryCount < _numberOfRetries)
+ {
+ // Give it a bit of time
+ await Task.Delay(100);
+ retryCount++;
+
+ goto retryDebug;
+ }
+ else
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Red;
+ OutputWriter.WriteLine($"Error connecting to debug engine on nanoDevice on {serialPort}.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return (device, ExitCodes.E2000, true);
+ }
+ }
+
+ retryCount = 0;
+
+ // initial check
+ if (device.DebugEngine.IsDeviceInInitializeState())
+ {
+ if (verbosity >= VerbosityLevel.Normal)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine($"Device status verified as being in initialized state. Requesting to resume execution. Attempt {retryCount}/{_numberOfRetries}.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ }
+
+ // set flag
+ deviceIsInInitializeState = true;
+
+ // device is still in initialization state, try resume execution
+ device.DebugEngine.ResumeExecution();
+ }
+
+ // handle the workflow required to try resuming the execution on the device
+ // only required if device is not already there
+ // retry 5 times with a 500ms interval between retries
+ while (retryCount++ < _numberOfRetries && deviceIsInInitializeState)
+ {
+ if (!device.DebugEngine.IsDeviceInInitializeState())
+ {
+ if (verbosity >= VerbosityLevel.Diagnostic)
+ {
+ OutputWriter.WriteLine($"Device has completed initialization.");
+ }
+
+ // done here
+ deviceIsInInitializeState = false;
+ break;
+ }
+
+ if (verbosity >= VerbosityLevel.Diagnostic)
+ {
+ OutputWriter.WriteLine($"Waiting for device to report initialization completed ({retryCount}/{_numberOfRetries}).");
+ }
+
+ // provide feedback to user on the 1st pass
+ if (retryCount == 0)
+ {
+ if (verbosity >= VerbosityLevel.Diagnostic)
+ {
+ OutputWriter.WriteLine($"Waiting for device to initialize.");
+ }
+ }
+
+ if (device.DebugEngine.IsConnectedTonanoBooter)
+ {
+ if (verbosity >= VerbosityLevel.Diagnostic)
+ {
+ OutputWriter.WriteLine($"Device reported running nanoBooter. Requesting to load nanoCLR.");
+ }
+
+ // request nanoBooter to load CLR
+ device.DebugEngine.ExecuteMemory(0);
+ }
+ else if (device.DebugEngine.IsConnectedTonanoCLR)
+ {
+ if (verbosity >= VerbosityLevel.Normal)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine($"Device reported running nanoCLR. Requesting to reboot nanoCLR.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ }
+
+ await Task.Run(delegate
+ {
+ // already running nanoCLR try rebooting the CLR
+ device.DebugEngine.RebootDevice(RebootOptions.ClrOnly);
+ });
+ }
+
+ // wait before next pass
+ // use a back-off strategy of increasing the wait time to accommodate slower or less responsive targets (such as networked ones)
+ await Task.Delay(TimeSpan.FromMilliseconds(_timeoutMiliseconds * (retryCount + 1)));
+
+ await Task.Yield();
+ }
+
+ return (device, ExitCodes.OK, deviceIsInInitializeState);
+ }
+ }
+}
diff --git a/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentManager.cs b/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentManager.cs
index 6c437463..38ded263 100644
--- a/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentManager.cs
+++ b/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentManager.cs
@@ -6,6 +6,7 @@
using System.Threading.Tasks;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.Extensions;
+using nanoFramework.Tools.FirmwareFlasher.DeploymentHelpers;
using Newtonsoft.Json;
namespace nanoFramework.Tools.FirmwareFlasher.FileDeployment
@@ -35,154 +36,11 @@ public FileDeploymentManager(string configFilePath, string originalPort, Verbosi
/// An ExitCode error.
public async Task DeployAsync()
{
- // number of retries when performing a deploy operation
- const int _numberOfRetries = 5;
- // timeout when performing a deploy operation
- const int _timeoutMiliseconds = 1000;
+ var (device, exitCode, deviceIsInInitializeState) = await DeviceHelper.ConnectDevice(_serialPort, _verbosity);
- NanoDeviceBase device = null;
- PortBase serialDebugClient;
- int retryCount = 0;
-
- serialDebugClient = PortBase.CreateInstanceForSerial(false);
-
- try
- {
- serialDebugClient.AddDevice(_serialPort);
-
- device = serialDebugClient.NanoFrameworkDevices[0];
- }
- catch (Exception)
- {
- OutputWriter.ForegroundColor = ConsoleColor.Red;
- OutputWriter.WriteLine($"Error connecting to nanoDevice on {_serialPort} to deploy files");
- OutputWriter.ForegroundColor = ConsoleColor.White;
- return ExitCodes.E2000;
- }
-
- // check if debugger engine exists
- if (device.DebugEngine == null)
- {
- device.CreateDebugEngine();
- if (_verbosity >= VerbosityLevel.Normal)
- {
- OutputWriter.WriteLine($"Debug engine created.");
- }
- }
-
- bool deviceIsInInitializeState = false;
-
- retryDebug:
- bool connectResult = device.DebugEngine.Connect(5000, true, true);
- if (retryCount == 0 && _verbosity >= VerbosityLevel.Normal)
- {
- OutputWriter.WriteLine($"Device connected and ready for file deployment.");
- }
- else if (_verbosity >= VerbosityLevel.Normal)
- {
- OutputWriter.WriteLine($"Device connect result is {connectResult}. Attempt {retryCount}/{_numberOfRetries}");
- }
-
- if (!connectResult)
- {
- if (retryCount < _numberOfRetries)
- {
- // Give it a bit of time
- await Task.Delay(100);
- retryCount++;
-
- goto retryDebug;
- }
- else
- {
- OutputWriter.ForegroundColor = ConsoleColor.Red;
- OutputWriter.WriteLine($"Error connecting to debug engine on nanoDevice on {_serialPort} to deploy files");
- OutputWriter.ForegroundColor = ConsoleColor.White;
- return ExitCodes.E2000;
- }
- }
-
- retryCount = 0;
-
- // initial check
- if (device.DebugEngine.IsDeviceInInitializeState())
- {
- if (_verbosity >= VerbosityLevel.Normal)
- {
- OutputWriter.ForegroundColor = ConsoleColor.Yellow;
- OutputWriter.WriteLine($"Device status verified as being in initialized state. Requesting to resume execution. Attempt {retryCount}/{_numberOfRetries}.");
- OutputWriter.ForegroundColor = ConsoleColor.White;
- }
-
- // set flag
- deviceIsInInitializeState = true;
-
- // device is still in initialization state, try resume execution
- device.DebugEngine.ResumeExecution();
- }
-
- // handle the workflow required to try resuming the execution on the device
- // only required if device is not already there
- // retry 5 times with a 500ms interval between retries
- while (retryCount++ < _numberOfRetries && deviceIsInInitializeState)
+ if (exitCode != ExitCodes.OK)
{
- if (!device.DebugEngine.IsDeviceInInitializeState())
- {
- if (_verbosity >= VerbosityLevel.Diagnostic)
- {
- OutputWriter.WriteLine($"Device has completed initialization.");
- }
-
- // done here
- deviceIsInInitializeState = false;
- break;
- }
-
- if (_verbosity >= VerbosityLevel.Diagnostic)
- {
- OutputWriter.WriteLine($"Waiting for device to report initialization completed ({retryCount}/{_numberOfRetries}).");
- }
-
- // provide feedback to user on the 1st pass
- if (retryCount == 0)
- {
- if (_verbosity >= VerbosityLevel.Diagnostic)
- {
- OutputWriter.WriteLine($"Waiting for device to initialize.");
- }
- }
-
- if (device.DebugEngine.IsConnectedTonanoBooter)
- {
- if (_verbosity >= VerbosityLevel.Diagnostic)
- {
- OutputWriter.WriteLine($"Device reported running nanoBooter. Requesting to load nanoCLR.");
- }
-
- // request nanoBooter to load CLR
- device.DebugEngine.ExecuteMemory(0);
- }
- else if (device.DebugEngine.IsConnectedTonanoCLR)
- {
- if (_verbosity >= VerbosityLevel.Normal)
- {
- OutputWriter.ForegroundColor = ConsoleColor.Yellow;
- OutputWriter.WriteLine($"Device reported running nanoCLR. Requesting to reboot nanoCLR.");
- OutputWriter.ForegroundColor = ConsoleColor.White;
- }
-
- await Task.Run(delegate
- {
- // already running nanoCLR try rebooting the CLR
- device.DebugEngine.RebootDevice(RebootOptions.ClrOnly);
- });
- }
-
- // wait before next pass
- // use a back-off strategy of increasing the wait time to accommodate slower or less responsive targets (such as networked ones)
- await Task.Delay(TimeSpan.FromMilliseconds(_timeoutMiliseconds * (retryCount + 1)));
-
- await Task.Yield();
+ return exitCode;
}
// check if device is still in initialized state
diff --git a/nanoFirmwareFlasher.Library/NetworkDeployment/Ethernet.cs b/nanoFirmwareFlasher.Library/NetworkDeployment/Ethernet.cs
new file mode 100644
index 00000000..c1cb32ec
--- /dev/null
+++ b/nanoFirmwareFlasher.Library/NetworkDeployment/Ethernet.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace nanoFramework.Tools.FirmwareFlasher.NetworkDeployment
+{
+ ///
+ /// Represents en ethernet configuration.
+ ///
+ public class Ethernet
+ {
+ ///
+ /// Gets or sets a value indicating whether DHCP is enabled.
+ ///
+ public bool DhcpEnabled { get; set; } = true;
+
+ ///
+ /// Gets or sets a value indicating whether automatic DNS is enabled.
+ ///
+ public bool AutomaticDNS { get; set; } = true;
+
+ ///
+ /// Gets or sets the IPv4 address.
+ ///
+ public string IPv4Address { get; set; }
+
+ ///
+ /// Gets or sets the IPv4 netmask.
+ ///
+ public string IPv4NetMask { get; set; }
+
+ ///
+ /// Gets or sets the IPv4 gateway.
+ ///
+ public string IPv4Gateway { get; set; }
+
+ ///
+ /// Gets or sets the primary IPv4 DNS address.
+ ///
+ public string IPv4DNSAddress1 { get; set; }
+
+ ///
+ /// Gets or sets the secondary IPv4 DNS address.
+ ///
+ public string IPv4DNSAddress2 { get; set; }
+
+ ///
+ /// Gets or setes the MAC address.
+ ///
+ public string MacAddress { get; set; }
+ }
+}
diff --git a/nanoFirmwareFlasher.Library/NetworkDeployment/NetworkDeploymentConfiguration.cs b/nanoFirmwareFlasher.Library/NetworkDeployment/NetworkDeploymentConfiguration.cs
new file mode 100644
index 00000000..33a857f3
--- /dev/null
+++ b/nanoFirmwareFlasher.Library/NetworkDeployment/NetworkDeploymentConfiguration.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace nanoFramework.Tools.FirmwareFlasher.NetworkDeployment
+{
+ ///
+ /// Represents the configuration for network deployment.
+ ///
+ internal class NetworkDeploymentConfiguration
+ {
+ ///
+ /// Gets or sets the serial port used for network deployment.
+ ///
+ public string SerialPort { get; set; }
+
+ ///
+ /// Gets or sets the wireless client configuration.
+ ///
+ public WirelessConfiguration WirelessClient { get; set; }
+
+ ///
+ /// Gets or sets the wireless access point configuration.
+ ///
+ public WirelessAccessPoint WirelessAccessPoint { get; set; }
+
+ ///
+ /// Gets or sets the ethernet configuration.
+ ///
+ public Ethernet Ethernet { get; set; }
+
+ ///
+ /// Gets or sets the device certificates.
+ ///
+ public string DeviceCertificates { get; set; }
+
+ ///
+ /// Gets or sets the path to the device certificates.
+ ///
+ public string DeviceCertificatesPath { get; set; }
+
+ ///
+ /// Gets or sets the CA certificates.
+ ///
+ public string CACertificates { get; set; }
+
+ ///
+ /// Gets or sets the path to the CA certificates.
+ ///
+ public string CACertificatesPath { get; set; }
+ }
+}
diff --git a/nanoFirmwareFlasher.Library/NetworkDeployment/NetworkDeploymentManager.cs b/nanoFirmwareFlasher.Library/NetworkDeployment/NetworkDeploymentManager.cs
new file mode 100644
index 00000000..347653bc
--- /dev/null
+++ b/nanoFirmwareFlasher.Library/NetworkDeployment/NetworkDeploymentManager.cs
@@ -0,0 +1,589 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using nanoFramework.Tools.Debugger;
+using nanoFramework.Tools.FirmwareFlasher.DeploymentHelpers;
+using Newtonsoft.Json;
+
+namespace nanoFramework.Tools.FirmwareFlasher.NetworkDeployment
+{
+ ///
+ /// Network Deployment Configuration class.
+ ///
+ public class NetworkDeploymentManager
+ {
+ private readonly VerbosityLevel _verbosity;
+ private readonly string _serialPort;
+ private readonly NetworkDeploymentConfiguration _configuration;
+
+ ///
+ /// Creates an instance of NetworkDeploymentManager.
+ ///
+ /// The configuration file path.
+ /// The original serial port used if none is provided in the json configuration.
+ /// The verbosity level.
+ public NetworkDeploymentManager(string configFilePath, string originalPort, VerbosityLevel verbosity)
+ {
+ _configuration = JsonConvert.DeserializeObject(File.ReadAllText(configFilePath));
+ _serialPort = string.IsNullOrEmpty(_configuration.SerialPort) ? originalPort : _configuration.SerialPort;
+ _verbosity = verbosity;
+ }
+
+ ///
+ /// Deploys async the network configuration.
+ ///
+ /// An ExitCode error.
+ public async Task DeployAsync()
+ {
+ var (device, exitCode, deviceIsInInitializeState) = await DeviceHelper.ConnectDevice(_serialPort, _verbosity);
+
+ if (exitCode != ExitCodes.OK)
+ {
+ return exitCode;
+ }
+
+ // check if device is still in initialized state
+ if (!deviceIsInInitializeState)
+ {
+ //var devConf = device.DebugEngine.GetDeviceConfiguration(new CancellationTokenSource(5000).Token);
+ var devConf = device.DebugEngine.GetDeviceConfiguration(default);
+
+ // Deploy the network configuration for woreless client
+ if (_configuration.WirelessClient != null)
+ {
+ // Read the configuration for the wifi interface
+ var wifiConfigurations = devConf.Wireless80211Configurations;
+ if (wifiConfigurations == null || wifiConfigurations.Count == 0)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Cannot find any wireless configuration.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ // So far, there is only 1 wifi interface, pick the first one
+ // Later, we can add more configurations if wanted
+ var wifiConfiguration = wifiConfigurations[0];
+ PopulateWirelessConfigurationProperties(wifiConfiguration, _configuration.WirelessClient);
+
+ // Find the wireless network configuration
+ (var networkConfigurationToSave, var blockId) = GetNetworkConfiguration(devConf, NetworkInterfaceType.Wireless80211);
+
+ if (networkConfigurationToSave == null)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Cannot find any network configuration for wireless client.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ PopulateNetworkProperties(networkConfigurationToSave, _configuration.WirelessClient);
+
+ try
+ {
+ if (!string.IsNullOrEmpty(_configuration.WirelessClient.MacAddress))
+ {
+ networkConfigurationToSave.MacAddress = GetMacAddress(_configuration.WirelessClient.MacAddress);
+ }
+ }
+ catch (Exception ex)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error converting MAC address:{ex}");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.Write($"Updating network configuration...");
+ if (device.DebugEngine.UpdateDeviceConfiguration(networkConfigurationToSave, blockId) != Engine.UpdateDeviceResult.Sucess)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error uploading network configuration.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.ForegroundColor = ConsoleColor.Green;
+ OutputWriter.WriteLine($"OK");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+
+ OutputWriter.Write($"Updating wireless configuration...");
+ if (device.DebugEngine.UpdateDeviceConfiguration(wifiConfiguration, 0) != Engine.UpdateDeviceResult.Sucess)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error uploading wireless configuration.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.ForegroundColor = ConsoleColor.Green;
+ OutputWriter.WriteLine($"OK");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ }
+
+ // Deploy Wireless Access Point configuration if any
+ if (_configuration.WirelessAccessPoint != null)
+ {
+ // Read the configuration for the wifi AP interface
+ var wifiApConfigurations = devConf.WirelessAPConfigurations;
+ if (wifiApConfigurations.Count == 0)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Cannot find any wireless AP configuration.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ var wifiApConfiguration = wifiApConfigurations[0];
+ PopulateWirelessConfigurationProperties(wifiApConfiguration, _configuration.WirelessAccessPoint);
+
+ // Those two ones are specific to the AP, default to None
+ wifiApConfiguration.WirelessAPOptions = GetWirelessAPOptions(_configuration.WirelessAccessPoint.AccessPointOptions ?? "None");
+ wifiApConfiguration.MaxConnections = _configuration.WirelessAccessPoint.MaxConnections;
+
+ // Find the wireless AP one
+ (var networkConfigurationToSave, var blockId) = GetNetworkConfiguration(devConf, NetworkInterfaceType.WirelessAP);
+
+ if (networkConfigurationToSave == null)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Cannot find any network configuration for wireless client.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ networkConfigurationToSave.StartupAddressMode = AddressMode.Static;
+ networkConfigurationToSave.IPv4Address = IPAddress.Parse(_configuration.WirelessAccessPoint.IPv4Address);
+ networkConfigurationToSave.IPv4NetMask = IPAddress.Parse(_configuration.WirelessAccessPoint.IPv4NetMask);
+ // We will use the IP addess of the device itself if no gateway provided
+ networkConfigurationToSave.IPv4GatewayAddress = IPAddress.Parse(string.IsNullOrEmpty(_configuration.WirelessAccessPoint.IPv4Gateway) ? _configuration.WirelessAccessPoint.IPv4Address : _configuration.WirelessClient.IPv4Gateway);
+
+ networkConfigurationToSave.IPv4DNSAddress1 = string.IsNullOrEmpty(_configuration.WirelessAccessPoint.IPv4DNSAddress1) ? IPAddress.None : IPAddress.Parse(_configuration.WirelessAccessPoint.IPv4DNSAddress1);
+ networkConfigurationToSave.IPv4DNSAddress2 = string.IsNullOrEmpty(_configuration.WirelessAccessPoint.IPv4DNSAddress2) ? IPAddress.None : IPAddress.Parse(_configuration.WirelessAccessPoint.IPv4DNSAddress2);
+
+ try
+ {
+ if (!string.IsNullOrEmpty(_configuration.WirelessAccessPoint.MacAddress))
+ {
+ networkConfigurationToSave.MacAddress = GetMacAddress(_configuration.WirelessAccessPoint.MacAddress);
+ }
+ }
+ catch (Exception ex)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error converting MAC address:{ex}");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.Write($"Updating network AP configuration...");
+ if (device.DebugEngine.UpdateDeviceConfiguration(networkConfigurationToSave, blockId) != Engine.UpdateDeviceResult.Sucess)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error uploading network configuration.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.ForegroundColor = ConsoleColor.Green;
+ OutputWriter.WriteLine($"OK");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+
+ OutputWriter.Write($"Updating wireless AP configuration...");
+ if (device.DebugEngine.UpdateDeviceConfiguration(wifiApConfiguration, 0) != Engine.UpdateDeviceResult.Sucess)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error uploading wireless configuration.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.ForegroundColor = ConsoleColor.Green;
+ OutputWriter.WriteLine($"OK");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ }
+
+ if (_configuration.Ethernet != null)
+ {
+ // Find the wireless one
+ (var networkConfigurationToSave, var blockId) = GetNetworkConfiguration(devConf, NetworkInterfaceType.Ethernet);
+
+ if (networkConfigurationToSave == null)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Cannot find any network configuration for ethernet.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ PopulateNetworkProperties(networkConfigurationToSave, _configuration.Ethernet);
+
+ try
+ {
+ if (!string.IsNullOrEmpty(_configuration.Ethernet.MacAddress))
+ {
+ networkConfigurationToSave.MacAddress = GetMacAddress(_configuration.Ethernet.MacAddress);
+ }
+ }
+ catch (Exception ex)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error converting MAC address:{ex}");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.Write($"Updating network ethernet configuration...");
+ if (device.DebugEngine.UpdateDeviceConfiguration(networkConfigurationToSave, blockId) != Engine.UpdateDeviceResult.Sucess)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error uploading network ethernet configuration.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.ForegroundColor = ConsoleColor.Green;
+ OutputWriter.WriteLine($"OK");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ }
+
+ // Deploy certificates if any
+ // First checks if we have a path or a device certificate
+ if (!string.IsNullOrEmpty(_configuration.DeviceCertificatesPath) && !string.IsNullOrEmpty(_configuration.DeviceCertificates))
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Both {nameof(_configuration.DeviceCertificatesPath)} and {nameof(_configuration.DeviceCertificates)} are set. Only one can be used.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ // Checks if have a path or to the CAcertificates
+ if (!string.IsNullOrEmpty(_configuration.CACertificatesPath) && !string.IsNullOrEmpty(_configuration.CACertificates))
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Both {nameof(_configuration.CACertificatesPath)} and {nameof(_configuration.CACertificates)} are set. Only one can be used.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ byte[] deviceCertificatesBytes = null;
+
+ if (!string.IsNullOrEmpty(_configuration.DeviceCertificates))
+ {
+ // Decode the Base64 encoded device certificates
+ deviceCertificatesBytes = Convert.FromBase64String(_configuration.DeviceCertificates);
+ }
+ else if (!string.IsNullOrEmpty(_configuration.DeviceCertificatesPath))
+ {
+ // Read the device certificates from the file
+ deviceCertificatesBytes = File.ReadAllBytes(_configuration.DeviceCertificatesPath);
+ }
+
+ CheckNullPemTermination(deviceCertificatesBytes);
+ if (deviceCertificatesBytes != null)
+ {
+ // deploy the client certificates
+ OutputWriter.Write($"Updating client certificates...");
+ var clientCertificates = device.DebugEngine.GetAllX509DeviceCertificates();
+ clientCertificates.Clear();
+ clientCertificates.Add(new DeviceConfiguration.X509DeviceCertificatesProperties()
+ {
+ Certificate = deviceCertificatesBytes,
+ CertificateSize = (uint)deviceCertificatesBytes.Length,
+ });
+
+ if (device.DebugEngine.UpdateDeviceConfiguration(clientCertificates, 0) != Engine.UpdateDeviceResult.Sucess)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error uploading device certificates.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.ForegroundColor = ConsoleColor.Green;
+ OutputWriter.WriteLine($"OK");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ }
+
+ byte[] caCertificatesBytes = null;
+
+ if (!string.IsNullOrEmpty(_configuration.CACertificates))
+ {
+ // Decode the Base64 encoded CA certificates
+ caCertificatesBytes = Convert.FromBase64String(_configuration.CACertificates);
+ }
+ else if (!string.IsNullOrEmpty(_configuration.CACertificatesPath))
+ {
+ // Read the CA certificates from the file
+ caCertificatesBytes = File.ReadAllBytes(_configuration.CACertificatesPath);
+ }
+
+ CheckNullPemTermination(caCertificatesBytes);
+ if (caCertificatesBytes != null)
+ {
+ // deploy the client certificates
+ OutputWriter.Write($"Updating client certificates...");
+ var caCertificates = device.DebugEngine.GetAllX509Certificates();
+ caCertificates.Clear();
+ caCertificates.Add(new DeviceConfiguration.X509CaRootBundleProperties()
+ {
+ Certificate = caCertificatesBytes,
+ CertificateSize = (uint)caCertificatesBytes.Length,
+ });
+
+ if (device.DebugEngine.UpdateDeviceConfiguration(caCertificates, 0) != Engine.UpdateDeviceResult.Sucess)
+ {
+ OutputWriter.ForegroundColor = ConsoleColor.Yellow;
+ OutputWriter.WriteLine();
+ OutputWriter.WriteLine($"Error uploading ca certificates.");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ return ExitCodes.E2002;
+ }
+
+ OutputWriter.ForegroundColor = ConsoleColor.Green;
+ OutputWriter.WriteLine($"OK");
+ OutputWriter.ForegroundColor = ConsoleColor.White;
+ }
+ }
+ else
+ {
+ return ExitCodes.E2002;
+ }
+
+ return ExitCodes.OK;
+ }
+
+ ///
+ /// Configures the authentication for the wireless client.
+ ///
+ /// The authentication type as a string.
+ /// The authentication type.
+ private AuthenticationType GetConfigureAuthentication(string authentication) => authentication.ToUpper() switch
+ {
+ "EAP" => AuthenticationType.EAP,
+ "PEAP" => AuthenticationType.PEAP,
+ "WCN" => AuthenticationType.WCN,
+ "OPEN" => AuthenticationType.Open,
+ "SHARED" => AuthenticationType.Shared,
+ "WEP" => AuthenticationType.WEP,
+ "WPA" => AuthenticationType.WPA,
+ "WPA2" => AuthenticationType.WPA2,
+ "NONE" => AuthenticationType.None,
+ _ => throw new ArgumentException($"{nameof(authentication)} must be either: None, EAP, PEPA, WCN, Open, Shared, WEP, WPA or WPA2"),
+ };
+
+ ///
+ /// Configures the wireless configuration options.
+ ///
+ /// The configuration option as a string.
+ /// The cifiguraiton option.
+ private Wireless80211_ConfigurationOptions GetConfigurationOptions(string configurationOpton) => configurationOpton.ToUpper() switch
+ {
+ "NONE" => Wireless80211_ConfigurationOptions.None,
+ "DISABLE" => Wireless80211_ConfigurationOptions.Disable,
+ "ENABLE" => Wireless80211_ConfigurationOptions.Enable,
+ "AUTOCONNECT" => Wireless80211_ConfigurationOptions.AutoConnect,
+ "SMARTCONFIG" => Wireless80211_ConfigurationOptions.SmartConfig,
+ _ => throw new ArgumentException($"{nameof(_configuration.WirelessClient.ConfigurationOption)} must be either: None, Disable, Enable, AutoConnect or SmartConfig"),
+ };
+
+ ///
+ /// Configures the radio type.
+ ///
+ /// The radio type as a string
+ /// The radio type.
+ private RadioType GetRadioType(string radioType) => radioType.ToUpper() switch
+ {
+ "802.11A" => RadioType._802_11a,
+ "802.11B" => RadioType._802_11b,
+ "802.11G" => RadioType._802_11g,
+ "802.11N" => RadioType._802_11n,
+ _ => throw new ArgumentException($"{nameof(radioType)} must be either: 802.11a, 802.11b, 802.11g or 802.11n"),
+ };
+
+ ///
+ /// Configures the encryption type.
+ ///
+ /// The encryption type as a string.
+ /// The encryption type.
+ private EncryptionType GetEncryptionType(string encryptionType) => encryptionType.ToUpper() switch
+ {
+ "WEP" => EncryptionType.WEP,
+ "WPA" => EncryptionType.WPA,
+ "WPA2" => EncryptionType.WPA2,
+ "WPA_PSK" => EncryptionType.WPA_PSK,
+ "WPA2_PSK2" => EncryptionType.WPA2_PSK2,
+ "CERTIFICATE" => EncryptionType.Certificate,
+ _ => EncryptionType.None,
+ };
+
+ private WirelessAP_ConfigurationOptions GetWirelessAPOptions(string wirelessAPOptions) => wirelessAPOptions.ToUpper() switch
+ {
+ "NONE" => WirelessAP_ConfigurationOptions.None,
+ "DISABLE" => WirelessAP_ConfigurationOptions.Disable,
+ "ENABLE" => WirelessAP_ConfigurationOptions.Enable,
+ "AUTOSTART" => WirelessAP_ConfigurationOptions.AutoStart,
+ "HIDDENSSID" => WirelessAP_ConfigurationOptions.HiddenSSID,
+ _ => throw new ArgumentException($"{nameof(wirelessAPOptions)} must be either: None, Disable, Enable, AutoStart or HiddenSSID"),
+ };
+
+ ///
+ /// Gets the MAC address.
+ ///
+ /// The MAC address as a string.
+ /// The MAC address as a byte array.
+ private byte[] GetMacAddress(string macAddress)
+ {
+ byte[] mac = new byte[6];
+ if (macAddress.Contains(":"))
+ {
+ var macParts = macAddress.Split(':');
+ for (int i = 0; i < 6; i++)
+ {
+ mac[i] = Convert.ToByte(macParts[i], 16);
+ }
+ }
+ else
+ {
+ if (macAddress.Length != 12)
+ {
+ throw new ArgumentException($"Mac address has to be 6 bytes, so 12 characters long.");
+ }
+
+ for (int i = 0; i < macAddress.Length; i += 2)
+ {
+ mac[i / 2] = Convert.ToByte(macAddress.Substring(i, 2), 16);
+ }
+ }
+
+ return mac;
+ }
+
+ ///
+ /// Gets the network configuration.
+ ///
+ /// The device configuration.
+ /// The network type to find.
+ ///
+ private (DeviceConfiguration.NetworkConfigurationProperties networkConfigurationToSave, uint blockId) GetNetworkConfiguration(DeviceConfiguration devConf, NetworkInterfaceType networkInterfaceType)
+ {
+ // Read the configuration for the network interfaces
+ var networkConfigurations = devConf.NetworkConfigurations;
+ // Find the wireless one
+ DeviceConfiguration.NetworkConfigurationProperties networkConfigurationToSave = null;
+ uint blockId = 0;
+ for (uint i = 0; i < networkConfigurations.Count; i++)
+ {
+ if (networkConfigurations[(int)i].InterfaceType == networkInterfaceType)
+ {
+ networkConfigurationToSave = networkConfigurations[(int)i];
+ blockId = i;
+ break;
+ }
+ }
+ return (networkConfigurationToSave, blockId);
+ }
+
+ ///
+ /// Populates the network properties.
+ ///
+ /// The network properties to populate.
+ /// The ethernet elements.
+ private void PopulateNetworkProperties(DeviceConfiguration.NetworkConfigurationProperties networkConfigurationToSave, Ethernet ethernet)
+ {
+ // Checks the IP Addesses if DHCP is set IPv4 from DHCP
+ if (ethernet.DhcpEnabled)
+ {
+ networkConfigurationToSave.StartupAddressMode = AddressMode.DHCP;
+ // clear remaining options
+ networkConfigurationToSave.IPv4Address = IPAddress.None;
+ networkConfigurationToSave.IPv4NetMask = IPAddress.None;
+ networkConfigurationToSave.IPv4GatewayAddress = IPAddress.None;
+ }
+ else
+ {
+ networkConfigurationToSave.StartupAddressMode = AddressMode.Static;
+ networkConfigurationToSave.IPv4Address = IPAddress.Parse(ethernet.IPv4Address);
+ networkConfigurationToSave.IPv4NetMask = IPAddress.Parse(ethernet.IPv4NetMask);
+ networkConfigurationToSave.IPv4GatewayAddress = IPAddress.Parse(ethernet.IPv4Gateway);
+ }
+
+ if (networkConfigurationToSave.AutomaticDNS)
+ {
+ // clear DNS addresses
+ networkConfigurationToSave.IPv4DNSAddress1 = IPAddress.None;
+ networkConfigurationToSave.IPv4DNSAddress2 = IPAddress.None;
+ }
+ else
+ {
+ networkConfigurationToSave.IPv4DNSAddress1 = IPAddress.Parse(ethernet.IPv4DNSAddress1);
+ networkConfigurationToSave.IPv4DNSAddress2 = IPAddress.Parse(ethernet.IPv4DNSAddress2);
+ }
+ }
+
+ ///
+ /// Checks if the PEM certificate has a null termination and add it if needed.
+ ///
+ /// The cert byte array.
+ private void CheckNullPemTermination(byte[] cert)
+ {
+ // Check if it's a PEM certificate starting with -----BEGIN CERTIFICATE-----
+ if(Encoding.ASCII.GetString(cert).Contains("-----BEGIN CERTIFICATE-----"))
+ {
+ // Check if the last byte is a null terminator
+ if (cert[cert.Length - 1] != 0)
+ {
+ // Add the PEM termination
+ Array.Resize(ref cert, cert.Length + 1);
+ cert[cert.Length - 1] = 0;
+ }
+ }
+ }
+
+ ///
+ /// Populates the wireless properties.
+ ///
+ /// The wireless property to populate
+ /// The wireless configuration
+ private void PopulateWirelessConfigurationProperties(WirelessConfigurationPropertiesBase wifiConfiguration, WirelessConfiguration wirelessClient)
+ {
+ if (!string.IsNullOrEmpty(wirelessClient.Authentication))
+ {
+ wifiConfiguration.Authentication = GetConfigureAuthentication(wirelessClient.Authentication);
+ }
+ wifiConfiguration.Password = wirelessClient.Password ?? string.Empty;
+ wifiConfiguration.Ssid = wirelessClient.Ssid;
+ if (!string.IsNullOrEmpty(wirelessClient.ConfigurationOption))
+ {
+ wifiConfiguration.Wireless80211Options = GetConfigurationOptions(wirelessClient.ConfigurationOption);
+ }
+ if (!string.IsNullOrEmpty(wirelessClient.RadioType))
+ {
+ wifiConfiguration.Radio = GetRadioType(wirelessClient.RadioType);
+ }
+ wifiConfiguration.Encryption = string.IsNullOrEmpty(wirelessClient.Encryption) ? EncryptionType.None : GetEncryptionType(wirelessClient.Encryption);
+ }
+ }
+}
diff --git a/nanoFirmwareFlasher.Library/NetworkDeployment/WirelessAccessPoint.cs b/nanoFirmwareFlasher.Library/NetworkDeployment/WirelessAccessPoint.cs
new file mode 100644
index 00000000..fba00fcb
--- /dev/null
+++ b/nanoFirmwareFlasher.Library/NetworkDeployment/WirelessAccessPoint.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace nanoFramework.Tools.FirmwareFlasher.NetworkDeployment
+{
+ ///
+ /// Represents a wireless access point configuration.
+ ///
+ public class WirelessAccessPoint : WirelessConfiguration
+ {
+ ///
+ /// Gets or sets the maximum number of connections.
+ ///
+ public byte MaxConnections { get; set; } = 4;
+
+ ///
+ /// Gets or sets the access point options.
+ ///
+ public string AccessPointOptions { get; set; }
+ }
+}
diff --git a/nanoFirmwareFlasher.Library/NetworkDeployment/WirelessConfiguration.cs b/nanoFirmwareFlasher.Library/NetworkDeployment/WirelessConfiguration.cs
new file mode 100644
index 00000000..e950dbad
--- /dev/null
+++ b/nanoFirmwareFlasher.Library/NetworkDeployment/WirelessConfiguration.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace nanoFramework.Tools.FirmwareFlasher.NetworkDeployment
+{
+ ///
+ /// Represents the wireless configuration.
+ ///
+ public class WirelessConfiguration: Ethernet
+ {
+ ///
+ /// Gets or sets the SSID of the wireless network.
+ ///
+ public string Ssid { get; set; }
+
+ ///
+ /// Gets or sets the password of the wireless network.
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// Gets or sets the authentication type.
+ ///
+ public string Authentication { get; set; }
+
+ ///
+ /// Gets or sets the encryption type.
+ ///
+ public string Encryption { get; set; }
+
+ ///
+ /// Gets or sets the configuration option.
+ ///
+ public string ConfigurationOption { get; set; }
+
+ ///
+ /// Gets or sets the radio type.
+ ///
+ public string RadioType { get; set; }
+ }
+}
diff --git a/nanoFirmwareFlasher.Tests/nanoFirmwareFlasher.Tests.csproj b/nanoFirmwareFlasher.Tests/nanoFirmwareFlasher.Tests.csproj
index 2128ab8e..e00d4261 100644
--- a/nanoFirmwareFlasher.Tests/nanoFirmwareFlasher.Tests.csproj
+++ b/nanoFirmwareFlasher.Tests/nanoFirmwareFlasher.Tests.csproj
@@ -20,6 +20,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/nanoFirmwareFlasher.Tool/Options.cs b/nanoFirmwareFlasher.Tool/Options.cs
index 665ed8f9..dd304de7 100644
--- a/nanoFirmwareFlasher.Tool/Options.cs
+++ b/nanoFirmwareFlasher.Tool/Options.cs
@@ -360,6 +360,13 @@ public class Options
HelpText = "JSON file containing file deployment settings.")]
public string FileDeployment { get; set; }
+ [Option(
+ "networkdeployment",
+ Required = false,
+ Default = null,
+ HelpText = "JSON file containing network deployment settings.")]
+ public string NetworkDeployment { get; set; }
+
[Option(
"archivepath",
Required = false,
diff --git a/nanoFirmwareFlasher.Tool/Program.cs b/nanoFirmwareFlasher.Tool/Program.cs
index 20b1028e..7f28b3f2 100644
--- a/nanoFirmwareFlasher.Tool/Program.cs
+++ b/nanoFirmwareFlasher.Tool/Program.cs
@@ -16,6 +16,7 @@
using Microsoft.Extensions.Configuration;
using nanoFramework.Tools.FirmwareFlasher.Extensions;
using nanoFramework.Tools.FirmwareFlasher.FileDeployment;
+using nanoFramework.Tools.FirmwareFlasher.NetworkDeployment;
using Newtonsoft.Json;
namespace nanoFramework.Tools.FirmwareFlasher
@@ -742,6 +743,8 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o)
#endregion
+ #region Files and Network deployment
+
// done nothing... or maybe not...
if (!operationPerformed && string.IsNullOrEmpty(o.FileDeployment))
{
@@ -764,6 +767,31 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o)
}
}
}
+
+ // done nothing... or maybe not...
+ if (!operationPerformed && string.IsNullOrEmpty(o.NetworkDeployment))
+ {
+ DisplayNoOperationMessage();
+ }
+ else
+ {
+ if ((_exitCode == ExitCodes.OK) && !string.IsNullOrEmpty(o.NetworkDeployment))
+ {
+ NetworkDeploymentManager deploy = new NetworkDeploymentManager(o.NetworkDeployment, o.SerialPort, _verbosityLevel);
+ try
+ {
+ _exitCode = await deploy.DeployAsync();
+ }
+ catch (Exception ex)
+ {
+ // exception with
+ _exitCode = ExitCodes.E2003;
+ _extraMessage = ex.Message;
+ }
+ }
+ }
+
+ #endregion
}
private static void DisplayNoOperationMessage()