-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #466 from WildernessLabs/bug/deprecated-flash
deprecate the flash os command - too complex to bother implementing t…
- Loading branch information
Showing
3 changed files
with
8 additions
and
329 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,344 +1,21 @@ | ||
using CliFx.Attributes; | ||
using Meadow.CLI.Core.Internals.Dfu; | ||
using Meadow.LibUsb; | ||
using Meadow.Software; | ||
using CliFx.Exceptions; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Meadow.CLI.Commands.DeviceManagement; | ||
|
||
[Command("flash os", Description = "** Deprecated ** Use `firmware write` instead")] | ||
public class FlashOsCommand : BaseDeviceCommand<FlashOsCommand> | ||
{ | ||
[CommandOption("osFile", 'o', Description = "Path to the Meadow OS binary")] | ||
public string OSFile { get; init; } = default!; | ||
|
||
[CommandOption("runtimeFile", 'r', Description = "Path to the Meadow Runtime binary")] | ||
public string RuntimeFile { get; init; } = default!; | ||
|
||
[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; } = default!; | ||
|
||
[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; } = default!; | ||
|
||
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, | ||
public FlashOsCommand( | ||
MeadowConnectionManager connectionManager, | ||
ILoggerFactory loggerFactory) | ||
: base(connectionManager, loggerFactory) | ||
{ | ||
Logger?.LogWarning($"Deprecated command. Use `firmware write` instead"); | ||
|
||
FileManager = fileManager; | ||
Settings = settingsManager; | ||
} | ||
|
||
protected override async ValueTask ExecuteCommand() | ||
{ | ||
var package = await GetSelectedPackage(); | ||
|
||
if (package == null) | ||
{ | ||
Logger?.LogError($"Unable to get selected OS package"); | ||
return; | ||
} | ||
|
||
var files = new List<FirmwareType>(); | ||
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 || connection.Device == null) | ||
{ | ||
return; | ||
} | ||
|
||
var cancellationToken = Console?.RegisterCancellationHandler(); | ||
|
||
if (Files.Any(f => f != FirmwareType.OS)) | ||
{ | ||
await connection.WaitForMeadowAttach(); | ||
|
||
await WriteFiles(); | ||
} | ||
|
||
var deviceInfo = await connection.Device.GetDeviceInfo(cancellationToken); | ||
|
||
if (deviceInfo != null) | ||
{ | ||
Logger?.LogInformation($"Done."); | ||
Logger?.LogInformation(deviceInfo.ToString()); | ||
} | ||
} | ||
else | ||
{ | ||
await WriteFiles(); | ||
} | ||
} | ||
|
||
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 - only connect one device"); | ||
} | ||
} | ||
|
||
private async Task<FirmwarePackage?> 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; | ||
} | ||
|
||
private async ValueTask WriteFiles() | ||
{ | ||
var connection = await GetCurrentConnection(); | ||
|
||
if (connection == null || connection.Device == null) | ||
{ | ||
return; | ||
} | ||
|
||
// 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 pack = await GetSelectedPackage(); | ||
|
||
if (pack == null) | ||
{ | ||
Logger?.LogError($"Unable to get selected OS package"); | ||
} | ||
FirmwarePackage package = pack!; | ||
|
||
var wasRuntimeEnabled = await connection.Device.IsRuntimeEnabled(CancellationToken); | ||
|
||
if (wasRuntimeEnabled) | ||
{ | ||
Logger?.LogInformation("Disabling device runtime..."); | ||
await connection.Device.RuntimeDisable(); | ||
} | ||
|
||
connection.FileWriteProgress += (s, e) => | ||
{ | ||
var p = (e.completed / (double)e.total) * 100d; | ||
Console?.Output.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 connection.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 connection.Device.WriteCoprocessorFiles(fileList, CancellationToken); | ||
} | ||
|
||
if (wasRuntimeEnabled) | ||
{ | ||
await connection.Device.RuntimeEnable(); | ||
} | ||
|
||
// TODO: if we're an F7 device, we need to reset | ||
} | ||
|
||
private async Task WriteOsWithDfu(string osFile, string serialNumber) | ||
protected override ValueTask ExecuteCommand() | ||
{ | ||
await DfuUtils.FlashFile( | ||
osFile, | ||
serialNumber, | ||
logger: Logger, | ||
format: DfuUtils.DfuFlashFormat.ConsoleOut); | ||
throw new CommandException($"Deprecated command. Use `firmware write` instead"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters