From eaa5d770a37b2a7bcd0016b5fbbb48c52753ebfe Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Tue, 3 Oct 2023 16:39:26 -0500 Subject: [PATCH] added legacy commands --- .../Commands/Current/BaseDeviceCommand.cs | 12 +- .../Commands/Current/Port/PortListCommand.cs | 14 +- .../Commands/Legacy/DownloadOsCommand.cs | 16 + .../Commands/Legacy/FlashOsCommand.cs | 326 ++++++++++++++++++ .../Commands/Legacy/InstallDfuCommand.cs | 2 +- .../Commands/Legacy/ListPortsCommand.cs | 22 ++ .../Commands/Legacy/MonoDisableCommand.cs | 2 +- .../Commands/Legacy/MonoStateCommand.cs | 14 + .../Commands/Legacy/UsePortCommand.cs | 30 ++ Source/v2/Meadow.Cli/Commands/commands.md | 20 +- .../Meadow.Cli/Properties/launchSettings.json | 24 ++ 11 files changed, 462 insertions(+), 20 deletions(-) create mode 100644 Source/v2/Meadow.Cli/Commands/Legacy/DownloadOsCommand.cs create mode 100644 Source/v2/Meadow.Cli/Commands/Legacy/FlashOsCommand.cs create mode 100644 Source/v2/Meadow.Cli/Commands/Legacy/ListPortsCommand.cs create mode 100644 Source/v2/Meadow.Cli/Commands/Legacy/MonoStateCommand.cs create mode 100644 Source/v2/Meadow.Cli/Commands/Legacy/UsePortCommand.cs diff --git a/Source/v2/Meadow.Cli/Commands/Current/BaseDeviceCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/BaseDeviceCommand.cs index f9201b1e..c53c0af6 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/BaseDeviceCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/BaseDeviceCommand.cs @@ -21,7 +21,17 @@ public BaseDeviceCommand(MeadowConnectionManager connectionManager, ILoggerFacto public virtual async ValueTask ExecuteAsync(IConsole console) { var cancellationToken = console.RegisterCancellationHandler(); - var c = ConnectionManager.GetCurrentConnection(); + + IMeadowConnection? c = null; + try + { + c = ConnectionManager.GetCurrentConnection(); + } + catch (Exception ex) + { + Logger.LogError($"Failed: {ex.Message}"); + return; + } if (c != null) { diff --git a/Source/v2/Meadow.Cli/Commands/Current/Port/PortListCommand.cs b/Source/v2/Meadow.Cli/Commands/Current/Port/PortListCommand.cs index 5d8f6b42..893740c7 100644 --- a/Source/v2/Meadow.Cli/Commands/Current/Port/PortListCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Current/Port/PortListCommand.cs @@ -1,8 +1,4 @@ -using System; -using CliFx.Attributes; -using CliFx.Infrastructure; -using Meadow.Hardware; -using Meadow.Hcom; +using CliFx.Attributes; using Microsoft.Extensions.Logging; namespace Meadow.CLI.Commands.DeviceManagement; @@ -23,11 +19,15 @@ protected override async ValueTask ExecuteCommand(CancellationToken? cancellatio if (Portlist.Count > 0) { var plural = Portlist.Count > 1 ? "s" : string.Empty; - Logger?.LogInformation($"Found the following device{plural} -"); + Logger?.LogInformation($"Found the following device{plural}:"); for (int i = 0; i < Portlist.Count; i++) { - Logger?.LogInformation($"{i + 1}: {Portlist[i]}"); + Logger?.LogInformation($" {i + 1}: {Portlist[i]}"); } } + else + { + Logger?.LogInformation($" No attached devices found"); + } } } \ No newline at end of file diff --git a/Source/v2/Meadow.Cli/Commands/Legacy/DownloadOsCommand.cs b/Source/v2/Meadow.Cli/Commands/Legacy/DownloadOsCommand.cs new file mode 100644 index 00000000..4c262134 --- /dev/null +++ b/Source/v2/Meadow.Cli/Commands/Legacy/DownloadOsCommand.cs @@ -0,0 +1,16 @@ +using CliFx.Attributes; +using Meadow.Cli; +using Meadow.Software; +using Microsoft.Extensions.Logging; + +namespace Meadow.CLI.Commands.DeviceManagement; + +[Command("download os", Description = "** Deprecated ** Use `firmware download` instead")] +public class DownloadOsCommand : FirmwareDownloadCommand +{ + public DownloadOsCommand(FileManager fileManager, ISettingsManager settingsManager, ILoggerFactory loggerFactory) + : base(fileManager, settingsManager, loggerFactory) + { + Logger?.LogWarning($"Deprecated command. Use `runtime disable` instead"); + } +} diff --git a/Source/v2/Meadow.Cli/Commands/Legacy/FlashOsCommand.cs b/Source/v2/Meadow.Cli/Commands/Legacy/FlashOsCommand.cs new file mode 100644 index 00000000..4fab67c1 --- /dev/null +++ b/Source/v2/Meadow.Cli/Commands/Legacy/FlashOsCommand.cs @@ -0,0 +1,326 @@ +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; + +namespace Meadow.CLI.Commands.DeviceManagement; + +[Command("flash os", Description = "** Deprecated ** Use `firmware write` instead")] +public class FlashOsCommand : BaseDeviceCommand +{ + [CommandOption("osFile", 'o', Description = "Path to the Meadow OS binary")] + public string OSFile { get; init; } + + [CommandOption("runtimeFile", 'r', Description = "Path to the Meadow Runtime binary")] + public string RuntimeFile { get; init; } + + [CommandOption("skipDfu", 'd', Description = "Skip DFU flash")] + public bool SkipOS { get; init; } + + [CommandOption("skipEsp", 'e', Description = "Skip ESP flash")] + public bool SkipEsp { get; init; } + + [CommandOption("skipRuntime", 'k', Description = "Skip updating the runtime")] + public bool SkipRuntime { get; init; } + + [CommandOption("dontPrompt", 'p', Description = "Don't show bulk erase prompt")] + public bool DontPrompt { get; init; } + + [CommandOption("osVersion", 'v', Description = "Flash a specific downloaded OS version - x.x.x.x")] + public string Version { get; private set; } + + private FirmwareType[]? Files { get; set; } = default!; + private bool UseDfu = true; + + private FileManager FileManager { get; } + private ISettingsManager Settings { get; } + + private ILibUsbDevice? _libUsbDevice; + + public FlashOsCommand(ISettingsManager settingsManager, FileManager fileManager, MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory) + : base(connectionManager, loggerFactory) + { + Logger?.LogWarning($"Deprecated command. Use `firmware write` instead"); + + FileManager = fileManager; + Settings = settingsManager; + } + + public override async ValueTask ExecuteAsync(IConsole console) + { + var package = await GetSelectedPackage(); + + var files = new List(); + if (!SkipOS) files.Add(FirmwareType.OS); + if (!SkipEsp) files.Add(FirmwareType.ESP); + if (!SkipRuntime) files.Add(FirmwareType.Runtime); + Files = files.ToArray(); + + if (Files == null) + { + Logger.LogInformation($"Writing all firmware for version '{package.Version}'..."); + + Files = new FirmwareType[] + { + FirmwareType.OS, + FirmwareType.Runtime, + FirmwareType.ESP + }; + } + + if (!Files.Contains(FirmwareType.OS) && UseDfu) + { + Logger.LogError($"DFU is only used for OS files. Select an OS file or remove the DFU option"); + return; + } + + bool deviceSupportsOta = false; // TODO: get this based on device OS version + + if (package.OsWithoutBootloader == null + || !deviceSupportsOta + || UseDfu) + { + UseDfu = true; + } + + + 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"? + + try + { + await WriteOsWithDfu(package.GetFullyQualifiedPath(package.OSWithBootloader), serial); + } + catch (Exception ex) + { + Logger.LogError($"Exception type: {ex.GetType().Name}"); + + // 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"); + + Logger.LogWarning("This machine requires an older version of libusb. Not to worry, I'll make the change for you, but you will have to re-run this 'firmware write' command."); + return; + } + + // 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) + { + if (retryCount++ > 10) + { + 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(); + + await ExecuteCommand(connection, connection.Device, cancellationToken); + } + + 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 GetSelectedPackage() + { + await FileManager.Refresh(); + + var collection = FileManager.Firmware["Meadow F7"]; + FirmwarePackage package; + + if (Version != null) + { + // make sure the requested version exists + var existing = collection.FirstOrDefault(v => v.Version == Version); + + if (existing == null) + { + Logger.LogError($"Requested version '{Version}' not found."); + return null; + } + package = existing; + } + else + { + Version = collection.DefaultPackage?.Version ?? + throw new Exception("No default version set"); + + package = collection.DefaultPackage; + } + + return package; + } + + protected override async ValueTask ExecuteCommand(IMeadowConnection connection, Hcom.IMeadowDevice device, CancellationToken cancellationToken) + { + // the connection passes messages back to us (info about actions happening on-device + connection.DeviceMessageReceived += (s, e) => + { + if (e.message.Contains("% downloaded")) + { + // don't echo this, as we're already reporting % written + } + else + { + Logger.LogInformation(e.message); + } + }; + connection.ConnectionMessage += (s, message) => + { + Logger.LogInformation(message); + }; + + var package = await GetSelectedPackage(); + + var wasRuntimeEnabled = await device.IsRuntimeEnabled(cancellationToken); + + if (wasRuntimeEnabled) + { + Logger.LogInformation("Disabling device runtime..."); + await device.RuntimeDisable(); + } + + connection.FileWriteProgress += (s, e) => + { + var p = (e.completed / (double)e.total) * 100d; + Console.Write($"Writing {e.fileName}: {p:0}% \r"); + }; + + if (Files.Contains(FirmwareType.OS)) + { + if (UseDfu) + { + // this would have already happened before now (in ExecuteAsync) so ignore + } + else + { + Logger.LogInformation($"{Environment.NewLine}Writing OS {package.Version}..."); + + throw new NotSupportedException("OtA writes for the OS are not yet supported"); + } + } + if (Files.Contains(FirmwareType.Runtime)) + { + Logger.LogInformation($"{Environment.NewLine}Writing Runtime {package.Version}..."); + + // get the path to the runtime file + var rtpath = package.GetFullyQualifiedPath(package.Runtime); + + // TODO: for serial, we must wait for the flash to complete + + await device.WriteRuntime(rtpath, cancellationToken); + } + if (Files.Contains(FirmwareType.ESP)) + { + Logger.LogInformation($"{Environment.NewLine}Writing Coprocessor files..."); + + var fileList = new string[] + { + package.GetFullyQualifiedPath(package.CoprocApplication), + package.GetFullyQualifiedPath(package.CoprocBootloader), + package.GetFullyQualifiedPath(package.CoprocPartitionTable), + }; + + await device.WriteCoprocessorFiles(fileList, cancellationToken); + } + + Logger.LogInformation($"{Environment.NewLine}"); + + if (wasRuntimeEnabled) + { + await device.RuntimeEnable(); + } + + // TODO: if we're an F7 device, we need to reset + } + + private async Task WriteOsWithDfu(string osFile, string serialNumber) + { + await DfuUtils.FlashFile( + osFile, + serialNumber, + logger: Logger, + format: DfuUtils.DfuFlashFormat.ConsoleOut); + } +} diff --git a/Source/v2/Meadow.Cli/Commands/Legacy/InstallDfuCommand.cs b/Source/v2/Meadow.Cli/Commands/Legacy/InstallDfuCommand.cs index 34b2f5f6..0f7d55d1 100644 --- a/Source/v2/Meadow.Cli/Commands/Legacy/InstallDfuCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Legacy/InstallDfuCommand.cs @@ -10,7 +10,7 @@ public class InstallDfuCommand : DfuInstallCommand public InstallDfuCommand(ISettingsManager settingsManager, ILoggerFactory loggerFactory) : base(settingsManager, loggerFactory, "0.11") { - Logger.LogWarning($"Deprecated command. Use `dfu install` instead"); + Logger?.LogWarning($"Deprecated command. Use `dfu install` instead"); } } diff --git a/Source/v2/Meadow.Cli/Commands/Legacy/ListPortsCommand.cs b/Source/v2/Meadow.Cli/Commands/Legacy/ListPortsCommand.cs new file mode 100644 index 00000000..99e645e5 --- /dev/null +++ b/Source/v2/Meadow.Cli/Commands/Legacy/ListPortsCommand.cs @@ -0,0 +1,22 @@ +using CliFx.Attributes; +using CliFx.Infrastructure; +using Meadow.Cli; +using Microsoft.Extensions.Logging; + +namespace Meadow.CLI.Commands.DeviceManagement; + +[Command("list ports", Description = "** Deprecated ** Use `port list` instead")] +public class ListPortsCommand : PortListCommand +{ + public ListPortsCommand(ISettingsManager settingsManager, ILoggerFactory loggerFactory) + : base(loggerFactory) + { + Logger?.LogWarning($"Deprecated command. Use `port list` instead"); + } + + public override ValueTask ExecuteAsync(IConsole console) + { + return base.ExecuteAsync(console); + } +} + diff --git a/Source/v2/Meadow.Cli/Commands/Legacy/MonoDisableCommand.cs b/Source/v2/Meadow.Cli/Commands/Legacy/MonoDisableCommand.cs index 0a743138..0a0807fe 100644 --- a/Source/v2/Meadow.Cli/Commands/Legacy/MonoDisableCommand.cs +++ b/Source/v2/Meadow.Cli/Commands/Legacy/MonoDisableCommand.cs @@ -9,6 +9,6 @@ public class MonoDisableCommand : RuntimeDisableCommand public MonoDisableCommand(MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory) : base(connectionManager, loggerFactory) { - Logger.LogWarning($"Deprecated command. Use `runtime disable` instead"); + Logger?.LogWarning($"Deprecated command. Use `runtime disable` instead"); } } diff --git a/Source/v2/Meadow.Cli/Commands/Legacy/MonoStateCommand.cs b/Source/v2/Meadow.Cli/Commands/Legacy/MonoStateCommand.cs new file mode 100644 index 00000000..02b08da3 --- /dev/null +++ b/Source/v2/Meadow.Cli/Commands/Legacy/MonoStateCommand.cs @@ -0,0 +1,14 @@ +using CliFx.Attributes; +using Microsoft.Extensions.Logging; + +namespace Meadow.CLI.Commands.DeviceManagement; + +[Command("mono state", Description = "** Deprecated ** Use `runtime state` instead")] +public class MonoStateCommand : RuntimeStateCommand +{ + public MonoStateCommand(MeadowConnectionManager connectionManager, ILoggerFactory loggerFactory) + : base(connectionManager, loggerFactory) + { + Logger.LogWarning($"Deprecated command. Use `runtime state` instead"); + } +} diff --git a/Source/v2/Meadow.Cli/Commands/Legacy/UsePortCommand.cs b/Source/v2/Meadow.Cli/Commands/Legacy/UsePortCommand.cs new file mode 100644 index 00000000..e6c06ee5 --- /dev/null +++ b/Source/v2/Meadow.Cli/Commands/Legacy/UsePortCommand.cs @@ -0,0 +1,30 @@ +using CliFx.Attributes; +using Meadow.Cli; +using Microsoft.Extensions.Logging; + +namespace Meadow.CLI.Commands.DeviceManagement; + +[Command("use port", Description = "** Deprecated ** Use `config route` instead")] +public class UsePortCommand : BaseCommand +{ + private ISettingsManager _settingsManager; + + [CommandParameter(0, Name = "Port", IsRequired = true)] + public string Port { get; set; } = default!; + + public UsePortCommand(ILoggerFactory loggerFactory, ISettingsManager settingsManager) + : base(loggerFactory) + { + Logger?.LogWarning($"Deprecated command. Use `config route` instead"); + _settingsManager = settingsManager; + } + + protected override ValueTask ExecuteCommand(CancellationToken? cancellationToken) + { + _settingsManager.SaveSetting("route", Port); + Logger?.LogInformation($"Using {Port}"); + + return ValueTask.CompletedTask; + } +} + diff --git a/Source/v2/Meadow.Cli/Commands/commands.md b/Source/v2/Meadow.Cli/Commands/commands.md index 8092816b..0d470be5 100644 --- a/Source/v2/Meadow.Cli/Commands/commands.md +++ b/Source/v2/Meadow.Cli/Commands/commands.md @@ -2,27 +2,27 @@ | v1 | v2 | Legacy Implemented | |---|---|---|---| -| list ports | port list | n | +| list ports | port list | y | | n/a | port select | - | | listen | listen | - | | device info | device info | - | | n/a | device reset | - | | n/a | device clock | - | -| use port | config route | n | +| use port | config route | y | | mono enable | runtime enable | y | | mono disable | runtime disable | y | -| mono state | runtime state | n | +| mono state | runtime state | y | | file list | file list | - | | file delete | file delete | - | | n/a | file read | - | | file write | file write | - | | file initial | file initial | - | | n/a | firmware list | - | -| download os | firmware download | n | +| download os | firmware download | y | | n/a | firmware default | - | | n/a | firmware delete | - | | flash esp | firmware write esp | n | -| flash os | firmware write os | n | +| flash os | firmware write os | y | | mono flash | flash write runtime | n | | mono update rt | flash write runtime | n | | trace enable | trace enable | - | @@ -32,16 +32,16 @@ | uart trace | uart trace | - | | n/a | app build | - | | n/a | app trim | - | -| install dfu-util | dfu install | n | +| install dfu-util | dfu install | y | | app deploy | app deploy | - | | n/a | app run | - | | flash erase | flash erase | - | | debug | TODO | | device provision | device provision | - | -| package create | cloud package create | n | -| package list | cloud package list | -| package publish | cloud package publish | -| package upload | cloud package upload | +| package create | cloud package create | - | +| package list | cloud package list | - | +| package publish | cloud package publish | - | +| package upload | cloud package upload | - | | collection list | cloud collection list | - | | cloud login | cloud login | - | | cloud logout | cloud logout | - | diff --git a/Source/v2/Meadow.Cli/Properties/launchSettings.json b/Source/v2/Meadow.Cli/Properties/launchSettings.json index 228ce42f..f43e3f07 100644 --- a/Source/v2/Meadow.Cli/Properties/launchSettings.json +++ b/Source/v2/Meadow.Cli/Properties/launchSettings.json @@ -230,6 +230,30 @@ "Cloud package publish": { "commandName": "Project", "commandLineArgs": "cloud package publish d867bb8b6e56418ba26ebe4e2b3ef6db -c 9d5f9780e6964c22b4ec15c44b886545" + }, + "legacy list ports": { + "commandName": "Project", + "commandLineArgs": "list ports" + }, + "legacy use port": { + "commandName": "Project", + "commandLineArgs": "use port COM1" + }, + "legacy mono state": { + "commandName": "Project", + "commandLineArgs": "mono state" + }, + "legacy mono enable": { + "commandName": "Project", + "commandLineArgs": "mono enable" + }, + "legacy mono disable": { + "commandName": "Project", + "commandLineArgs": "mono disable" + }, + "legacy download os": { + "commandName": "Project", + "commandLineArgs": "download os" } } } \ No newline at end of file