Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 #362

Merged
merged 12 commits into from
Sep 29, 2023
22 changes: 20 additions & 2 deletions Source/v2/Meadow.CLI.v2.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Logging", "..\..\..\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Units", "..\..\..\Meadow.Units\Source\Meadow.Units\Meadow.Units.csproj", "{5DC0D8EC-85D4-4E98-9403-2D5B9700D8AC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meadow.SoftwareManager", "Meadow.SoftwareManager\Meadow.SoftwareManager.csproj", "{B842E44B-F57F-4728-980F-776DE6E2CDAA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.SoftwareManager", "Meadow.SoftwareManager\Meadow.SoftwareManager.csproj", "{B842E44B-F57F-4728-980F-776DE6E2CDAA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meadow.SoftwareManager.Unit.Tests", "Meadow.SoftwareManager.Unit.Tests\Meadow.SoftwareManager.Unit.Tests.csproj", "{B093D40B-AA2F-4BA8-9B7E-DB14DAC9AFC9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.SoftwareManager.Unit.Tests", "Meadow.SoftwareManager.Unit.Tests\Meadow.SoftwareManager.Unit.Tests.csproj", "{B093D40B-AA2F-4BA8-9B7E-DB14DAC9AFC9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meadow.UsbLib", "Meadow.UsbLib\Meadow.UsbLib.csproj", "{5B071B58-550C-492D-8A24-973D4183FAAF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meadow.UsbLibClassic", "Meadow.UsbLibClassic\Meadow.UsbLibClassic.csproj", "{42F3A4D2-CBF9-4DF3-8ADC-BF775101F34A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meadow.Cli.Core", "Meadow.Cli.Core\Meadow.Cli.Core.csproj", "{677B1C15-8936-4807-8A4F-4F5219BBDB7C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -63,6 +69,18 @@ Global
{B093D40B-AA2F-4BA8-9B7E-DB14DAC9AFC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B093D40B-AA2F-4BA8-9B7E-DB14DAC9AFC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B093D40B-AA2F-4BA8-9B7E-DB14DAC9AFC9}.Release|Any CPU.Build.0 = Release|Any CPU
{5B071B58-550C-492D-8A24-973D4183FAAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B071B58-550C-492D-8A24-973D4183FAAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B071B58-550C-492D-8A24-973D4183FAAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B071B58-550C-492D-8A24-973D4183FAAF}.Release|Any CPU.Build.0 = Release|Any CPU
{42F3A4D2-CBF9-4DF3-8ADC-BF775101F34A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42F3A4D2-CBF9-4DF3-8ADC-BF775101F34A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42F3A4D2-CBF9-4DF3-8ADC-BF775101F34A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42F3A4D2-CBF9-4DF3-8ADC-BF775101F34A}.Release|Any CPU.Build.0 = Release|Any CPU
{677B1C15-8936-4807-8A4F-4F5219BBDB7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{677B1C15-8936-4807-8A4F-4F5219BBDB7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{677B1C15-8936-4807-8A4F-4F5219BBDB7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{677B1C15-8936-4807-8A4F-4F5219BBDB7C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
11 changes: 11 additions & 0 deletions Source/v2/Meadow.Cli.Core/ILibUsbDevice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Meadow.LibUsb;

public interface ILibUsbProvider
{
List<ILibUsbDevice> GetDevicesInBootloaderMode();
}

public interface ILibUsbDevice
{
string GetDeviceSerialNumber();
}
13 changes: 13 additions & 0 deletions Source/v2/Meadow.Cli.Core/Meadow.Cli.Core.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
</ItemGroup>

</Project>
111 changes: 99 additions & 12 deletions Source/v2/Meadow.Cli/Commands/Current/Firmware/FirmwareWriteCommand.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using CliFx.Attributes;
using CliFx.Infrastructure;
using Meadow.Cli;
using Meadow.CLI.Core.Internals.Dfu;
using Meadow.Hcom;
using Meadow.LibUsb;
using Meadow.Software;
using Microsoft.Extensions.Logging;

Expand All @@ -14,7 +16,7 @@ public enum FirmwareType
ESP
}

[Command("firmware write", Description = "Download a firmware package")]
[Command("firmware write", Description = "Writes firmware files to a connected meadow device")]
public class FirmwareWriteCommand : BaseDeviceCommand<FirmwareWriteCommand>
{
[CommandOption("version", 'v', IsRequired = false)]
Expand All @@ -27,11 +29,15 @@ public class FirmwareWriteCommand : BaseDeviceCommand<FirmwareWriteCommand>
public FirmwareType[]? Files { get; set; } = default!;

private FileManager FileManager { get; }
private ISettingsManager Settings { get; }

public FirmwareWriteCommand(FileManager fileManager, MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory)
private ILibUsbDevice? _libUsbDevice;

public FirmwareWriteCommand(ISettingsManager settingsManager, FileManager fileManager, MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory)
: base(connectionManager, loggerFactory)
{
FileManager = fileManager;
Settings = settingsManager;
}

public override async ValueTask ExecuteAsync(IConsole console)
Expand Down Expand Up @@ -68,33 +74,113 @@ public override async ValueTask ExecuteAsync(IConsole console)

if (UseDfu && Files.Contains(FirmwareType.OS))
{
// get a list of ports - it will not have our meadow in it (since it should be in DFU mode)
var initialPorts = await MeadowConnectionManager.GetSerialPorts();

// get the device's serial number via DFU - we'll need it to find the device after it resets
try
{
_libUsbDevice = GetLibUsbDeviceForCurrentEnvironment();
}
catch (Exception ex)
{
Logger.LogError(ex.Message);
return;
}

var serial = _libUsbDevice.GetDeviceSerialNumber();

// no connection is required here - in fact one won't exist
// unless maybe we add a "DFUConnection"?
await WriteOsWithDfu(package.GetFullyQualifiedPath(package.OSWithBootloader));

// TODO: if the user requested flashing more than the OS, we have to wait for a connection and then proceed with that
if (Files.Any(f => f != FirmwareType.OS))
try
{
await WriteOsWithDfu(package.GetFullyQualifiedPath(package.OSWithBootloader), serial);
}
catch (Exception ex)
{
// TODO: scope this to the right exception type for Win 10 access violation thing
// TODO: catch the Win10 DFU error here and change the global provider configuration to "classic"
Settings.SaveSetting(SettingsManager.PublicSettings.LibUsb, "classic");
}

// now wait for a new serial port to appear
var ports = await MeadowConnectionManager.GetSerialPorts();
var retryCount = 0;

var newPort = ports.Except(initialPorts).FirstOrDefault();
while (newPort == null)
{
var connection = ConnectionManager.GetCurrentConnection();
if (connection == null)
if (retryCount++ > 10)
{
Logger.LogError($"No connection path is defined");
return;
throw new Exception("New meadow device not found");
}
await Task.Delay(500);
ports = await MeadowConnectionManager.GetSerialPorts();
newPort = ports.Except(initialPorts).FirstOrDefault();
}

// configure the route to that port for the user
Settings.SaveSetting(SettingsManager.PublicSettings.Route, newPort);

var connection = ConnectionManager.GetCurrentConnection();
if (connection == null)
{
Logger.LogError($"No connection path is defined");
return;
}

var cancellationToken = console.RegisterCancellationHandler();

if (Files.Any(f => f != FirmwareType.OS))
{
await connection.WaitForMeadowAttach();

var cancellationToken = console.RegisterCancellationHandler();
await ExecuteCommand(connection, connection.Device, cancellationToken);
}
Logger.LogInformation($"Done.");

var deviceInfo = await connection.Device.GetDeviceInfo(cancellationToken);

if (deviceInfo != null)
{
Logger.LogInformation($"Done.");
Logger.LogInformation(deviceInfo.ToString());
}
}
else
{
await base.ExecuteAsync(console);
}
}

private ILibUsbDevice GetLibUsbDeviceForCurrentEnvironment()
{
ILibUsbProvider provider;

// TODO: read the settings manager to decide which provider to use (default to non-classic)
var setting = Settings.GetAppSetting(SettingsManager.PublicSettings.LibUsb);
if (setting == "classic")
{
provider = new ClassicLibUsbProvider();
}
else
{
provider = new LibUsbProvider();
}

var devices = provider.GetDevicesInBootloaderMode();

switch (devices.Count)
{
case 0:
throw new Exception("No device found in bootloader mode");
case 1:
return devices[0];
default:
throw new Exception("Multiple devices found in bootloader mode. Disconnect all but one");
}
}

private async Task<FirmwarePackage?> GetSelectedPackage()
{
await FileManager.Refresh();
Expand Down Expand Up @@ -208,10 +294,11 @@ protected override async ValueTask ExecuteCommand(IMeadowConnection connection,
// TODO: if we're an F7 device, we need to reset
}

private async Task WriteOsWithDfu(string osFile)
private async Task WriteOsWithDfu(string osFile, string serialNumber)
{
await DfuUtils.FlashFile(
osFile,
serialNumber,
logger: Logger,
format: DfuUtils.DfuFlashFormat.ConsoleOut);
}
Expand Down
99 changes: 12 additions & 87 deletions Source/v2/Meadow.Cli/DFU/DfuUtils.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using LibUsbDotNet.LibUsb;
//using LibUsbDotNet.LibUsb;
using Meadow.Hcom;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
Expand All @@ -12,71 +12,8 @@ namespace Meadow.CLI.Core.Internals.Dfu;
public static class DfuUtils
{
private static int _osAddress = 0x08000000;
private static string _usbStmName = "STM32 BOOTLOADER";
private static int _usbBootLoaderVenderID = 1155; // Equivalent to _usbStmName but for the LibUsbDotNet 3.x

public static string LastSerialNumber { get; private set; } = "";

public static bool CheckForValidDevice()
{
try
{
GetDeviceInBootloaderMode();
return true;
}
catch (Exception)
{
return false;
}
}

public static IUsbDevice GetDeviceInBootloaderMode()
{
var allDevices = GetDevicesInBootloaderMode();
if (allDevices.Count() > 1)
{
throw new Exception("More than one DFU device found, please connect only one and try again.");
}

var device = allDevices.SingleOrDefault();
if (device == null)
{
throw new Exception("Device not found. Connect a device in bootloader mode. If the device is in bootloader mode, please update the device driver. See instructions at https://wldrn.es/usbdriver");
}

return device;
}

public static IEnumerable<IUsbDevice> GetDevicesInBootloaderMode()
{
using (UsbContext context = new UsbContext())
{
var allDevices = context.List();
var ourDevices = allDevices.Where(d => d.Info.VendorId == _usbBootLoaderVenderID);
if (ourDevices.Count() < 1)
{
throw new Exception("No Devices found. Connect a device in bootloader mode. If the device is in bootloader mode, please update the device driver. See instructions at https://wldrn.es/usbdriver");
}
return ourDevices;
}
}

public static string GetDeviceSerial(IUsbDevice device)
{
var serialNumber = string.Empty;

if (device != null)
{
device.Open();
if (device.IsOpen)
{
serialNumber = device.Info?.SerialNumber;
device.Close();
}
}

return serialNumber ?? string.Empty;
}
// public static string LastSerialNumber { get; private set; } = "";

public enum DfuFlashFormat
{
Expand Down Expand Up @@ -126,33 +63,22 @@ private static void FormatDfuOutput(string logLine, ILogger? logger, DfuFlashFor
}
}

public static async Task<bool> FlashFile(string fileName, IUsbDevice? device = null, ILogger? logger = null, DfuFlashFormat format = DfuFlashFormat.Percent)
public static async Task<bool> FlashFile(string fileName, string dfuSerialNumber, ILogger? logger = null, DfuFlashFormat format = DfuFlashFormat.Percent)
{
logger ??= NullLogger.Instance;
device ??= GetDeviceInBootloaderMode();

if (!File.Exists(fileName))
{
logger.LogError($"Unable to flash {fileName} - file or folder does not exist");
return false;
}

if (!File.Exists(fileName))
{
logger.LogError($"Unable to find file '{fileName}'. Please specify valid --File or download the latest with: meadow download os");
return false;
}
else
{
logger.LogInformation($"Flashing OS with {fileName}");
}

LastSerialNumber = GetDeviceSerial(device);
logger.LogInformation($"Flashing OS with {fileName}");

var dfuUtilVersion = GetDfuUtilVersion();
var dfuUtilVersion = new System.Version(GetDfuUtilVersion());
logger.LogDebug("Detected OS: {os}", RuntimeInformation.OSDescription);

if (string.IsNullOrEmpty(dfuUtilVersion))
if (dfuUtilVersion == null)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Expand All @@ -164,24 +90,23 @@ public static async Task<bool> FlashFile(string fileName, IUsbDevice? device = n
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
logger.LogError("dfu-util not found - install using package manager, for example: `apt install dfu-util`");
logger.LogError("dfu-util not found - install using package manager, for example: `apt install dfu-util` or the equivalent for your Linux distribution");
}
return false;
}
else if (dfuUtilVersion != "0.10")
else if (dfuUtilVersion.CompareTo(new System.Version("0.11")) < 0)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
logger.LogError("dfu-util update required. To install, run in administrator mode: meadow install dfu-util");
logger.LogError("dfu-util update required. To update, run in administrator mode: `meadow install dfu-util`");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
logger.LogError("dfu-util update required. To install, run: brew upgrade dfu-util");
logger.LogError("dfu-util update required. To update, run: `brew upgrade dfu-util`");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
if (dfuUtilVersion != "0.9")
return false;
logger.LogError("dfu-util update required. To update , run: `apt upgrade dfu-util` or the equivalent for your Linux distribution");
}
else
{
Expand All @@ -191,7 +116,7 @@ public static async Task<bool> FlashFile(string fileName, IUsbDevice? device = n

try
{
var args = $"-a 0 -S {LastSerialNumber} -D \"{fileName}\" -s {_osAddress}:leave";
var args = $"-a 0 -S {dfuSerialNumber} -D \"{fileName}\" -s {_osAddress}:leave";

await RunDfuUtil(args, logger, format);
}
Expand Down
2 changes: 1 addition & 1 deletion Source/v2/Meadow.Cli/ISettingsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
public interface ISettingsManager
{
void DeleteSetting(string setting);
string? GetAppSetting(string name);
string? GetAppSetting(string name, string? defaultValue = null);
Dictionary<string, string> GetPublicSettings();
string? GetSetting(string setting);
void SaveSetting(string setting, string value);
Expand Down
Loading
Loading