using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; using Tango.Core; namespace Tango.FirmwareUpdateLib.WPF { public class FirmwareUpgradeManager : ExtendedObject { private FirmwareUpdateManager _updater; private List _current_devices; #region Events /// /// Reports about the progress of an on-going firmware upgrade. /// public event EventHandler UpgradeProgress; #endregion #region Properties private FirmwareUpgradeManagerState _state; /// /// Gets or sets the current manager state. /// public FirmwareUpgradeManagerState State { get { return _state; } private set { _state = value; RaisePropertyChangedAuto(); } } #endregion /// /// Initializes a new instance of the class. /// public FirmwareUpgradeManager() { _current_devices = new List(); } /// /// Performs a full automatic upgrade by getting the first DFU device switching to DFU mode if necessary. /// Uploading, verifying, resetting the device, then checking device existence. /// /// The MCU bin file path. /// public Task PerformUpgrade(byte[] mcuData) { EnsureStateIdle(); State = FirmwareUpgradeManagerState.Busy; TaskCompletionSource taskSource = new TaskCompletionSource(); ProgressDispatcher progressDispatcher = new ProgressDispatcher(); Task.Factory.StartNew(() => { try { OnUpgradeProgressChanged(100, 0, FirmwareUpgradeManagerState.Initializing); Initialize(); Thread.Sleep(1000); LogManager.Log("Initializing progress dispatcher..."); OnUpgradeProgressChanged(100, 10, State); LogManager.Log("Starting automatic full upgrade..."); OnUpgradeProgressChanged(100, 20, FirmwareUpgradeManagerState.EnumeratingDevices); var device = GetAvailableDevices().FirstOrDefault(); Thread.Sleep(1000); State = FirmwareUpgradeManagerState.Busy; OnUpgradeProgressChanged(100, 30, State); Thread.Sleep(1000); if (device == null) { throw LogManager.Log(new NullReferenceException("No DFU device found on the system.")); } LogManager.Log("Found DFU device..."); LogManager.Log($"Name: {device.DeviceName}"); LogManager.Log($"Mode: {device.Mode}"); if (device.Mode == DFUMode.RunTime) { State = FirmwareUpgradeManagerState.SwitchingToDFU; LogManager.Log("Switching to DFU mode..."); OnUpgradeProgressChanged(100, 40, State); Thread.Sleep(1000); device.SwitchToDFUMode(); LogManager.Log("Waiting for the device to comeback (3 sec)..."); State = FirmwareUpgradeManagerState.Waiting; OnUpgradeProgressChanged(100, 50, State); Thread.Sleep(3000); LogManager.Log("Looking for the modified DFU device..."); OnUpgradeProgressChanged(100, 60, FirmwareUpgradeManagerState.EnumeratingDevices); Thread.Sleep(1000); device = GetAvailableDevices().FirstOrDefault(); State = FirmwareUpgradeManagerState.SwitchingToDFU; OnUpgradeProgressChanged(100, 60, State); if (device == null) { throw LogManager.Log(new NullReferenceException("Could not locate the modified DFU device.")); } if (device.Mode == DFUMode.RunTime) { throw LogManager.Log(new InvalidOperationException("The DFU device was found but has failed to switch into DFU mode.")); } LogManager.Log("DFU device successfully switched to DFU mode."); LogManager.Log($"Name: {device.DeviceName}"); LogManager.Log($"Mode: {device.Mode}"); } else { LogManager.Log("Device is in DFU mode. Skipping DFU switching..."); } State = FirmwareUpgradeManagerState.StartingUpload; OnUpgradeProgressChanged(100, 100, State); LogManager.Log("Starting DFU upload..."); ProgressMode current_mode = ProgressMode.Verifying; progressDispatcher.Initialize(); progressDispatcher.Invoke(() => { device.Upload(mcuData, (mode, current, total) => { try { if (current_mode != mode) { current_mode = mode; LogManager.Log($"DFU Upload Status: {current_mode}"); } if (mode == ProgressMode.Uploading) { OnUpgradeProgressChanged(total, current, FirmwareUpgradeManagerState.Uploading); } else if (mode == ProgressMode.Verifying) { OnUpgradeProgressChanged(total, current, FirmwareUpgradeManagerState.Verifying); } if (mode == ProgressMode.Error) { throw LogManager.Log(new ApplicationException("DFU upload got an error notification!")); } else if (mode == ProgressMode.Completed) { LogManager.Log("DFU upload completed successfully."); LogManager.Log("Resetting the device..."); State = FirmwareUpgradeManagerState.Resetting; OnUpgradeProgressChanged(100, 0, State); device.Reset(); LogManager.Log("Waiting for the device to comeback (3 sec)..."); State = FirmwareUpgradeManagerState.Waiting; OnUpgradeProgressChanged(100, 30, State); Thread.Sleep(3000); LogManager.Log("Looking for the modified DFU device..."); OnUpgradeProgressChanged(100, 70, FirmwareUpgradeManagerState.EnumeratingDevices); device = GetAvailableDevices().FirstOrDefault(); Thread.Sleep(1000); State = FirmwareUpgradeManagerState.Busy; OnUpgradeProgressChanged(100, 90, State); Thread.Sleep(1000); if (device == null) { throw LogManager.Log(new NullReferenceException("Could not locate the modified DFU device.")); } if (device.Mode == DFUMode.DFU) { throw LogManager.Log(new InvalidOperationException("The DFU device was found but has failed to switch back into RunTime mode.")); } LogManager.Log("DFU device successfully upgraded!"); LogManager.Log($"Name: {device.DeviceName}"); LogManager.Log($"Mode: {device.Mode}"); State = FirmwareUpgradeManagerState.Completed; OnUpgradeProgressChanged(100, 100, State); taskSource.SetResult(true); progressDispatcher.Close(); } DoEvents(); } catch (Exception ex) { taskSource.SetException(ex); progressDispatcher.Close(); } finally { State = FirmwareUpgradeManagerState.Idle; } }); }); } catch (Exception ex) { taskSource.SetException(ex); } finally { State = FirmwareUpgradeManagerState.Idle; } }); return taskSource.Task; } /// /// Ensures the state idle. /// /// private void EnsureStateIdle() { if (State != FirmwareUpgradeManagerState.Idle) throw new InvalidOperationException($"Operation can be performed only on {FirmwareUpgradeManagerState.Idle} state."); } /// /// Gets the available devices. /// /// private List GetAvailableDevices() { State = FirmwareUpgradeManagerState.EnumeratingDevices; LogManager.Log("Disposing previous devices..."); foreach (var device in _current_devices) { try { LogManager.Log($"Disposing previous device {device.DeviceName}..."); device.Dispose(); } catch { } } try { LogManager.Log("Enumerating available DFU devices..."); _current_devices = _updater.GetAvailableDevices(false).Where(x => !x.DeviceName.Contains("In-Circuit Debug Interface")).ToList(); } catch (Exception ex) { throw LogManager.Log(ex, "Error enumerating available DFU devices."); } return _current_devices; } /// /// Initializes this instance. /// public void Initialize() { State = FirmwareUpgradeManagerState.Initializing; try { LogManager.Log("Initializing firmware upgrade API..."); _updater = new FirmwareUpdateManager(); _updater.Initialize(); } catch (Exception ex) { throw LogManager.Log(ex, "Error initializing firmware upgrade API."); } } private void DoEvents() { Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); } /// /// Raises the event. /// /// The total. /// The progress. /// The state. protected virtual void OnUpgradeProgressChanged(double total, double progress, FirmwareUpgradeManagerState state) { UpgradeProgress?.Invoke(this, new FirmwareUpgradeProgressEventArgs() { Progress = progress, Total = total, State = state, }); } } }