aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Tango.PPC.Common
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy@twine-s.com>2020-12-30 15:11:34 +0000
committerRoy Ben Shabat <Roy@twine-s.com>2020-12-30 15:11:34 +0000
commitd33c19b3ac6803de4b5c8d475832efef131c1a45 (patch)
treeea725abc39def99a755b041c13cba1fe0d594ddc /Software/Visual_Studio/PPC/Tango.PPC.Common
parent1bdcaa9f51303bbff682507f31fb3b4414692ca4 (diff)
downloadTango-d33c19b3ac6803de4b5c8d475832efef131c1a45.tar.gz
Tango-d33c19b3ac6803de4b5c8d475832efef131c1a45.zip
Revert "Hope it is fine"
Diffstat (limited to 'Software/Visual_Studio/PPC/Tango.PPC.Common')
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Application/IPPCApplicationManager.cs31
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Authentication/IAuthenticationProvider.cs11
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs81
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupMode.cs23
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreProgressEventArgs.cs36
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs63
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupSettings.cs19
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs604
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs43
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs31
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs136
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/IMachineProvider.cs15
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Connectivity/IConnectivityProvider.cs7
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Console/DefaultConsoleEngineService.cs92
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Console/IConsoleEngineService.cs16
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Controls/ImageGalleryControl.cs114
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Controls/ImageGalleryControl.xaml101
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/DataStore/DefaultDataStoreService.cs492
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/DataStore/IDataStoreService.cs14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs61
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/IEventLogger.cs3
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/ExternalBridge/PPCExternalBridgeService.cs36
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs433
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/FileSystemOperation.cs51
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/FileSystemOperationMode.cs (renamed from Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/UpdatePackageFile.cs)7
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs20
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Helpers/KeyboardHelper.cs24
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Helpers/LogsHelper.cs24
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/IPPCService.cs36
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Images/cl-full.pngbin0 -> 3454 bytes
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Images/lubricant2.pngbin0 -> 194234 bytes
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs154
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/IInsightsService.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/MachineSetup/MachineSetupManager.cs200
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs59
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs1451
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Models/FineTuneItem.cs2
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationManager.cs17
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/NavigationView.cs3
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarItem.cs10
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarPriority.cs15
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/INotificationProvider.cs16
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItem.cs25
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItem.cs3
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItemView.xaml14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/OS/DefaultOperationSystemManager.cs22
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/OS/IOperationSystemManager.cs11
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs190
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs50
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/DefaultPerformanceService.cs219
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/IPerformanceService.cs14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.Designer.cs22
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.resx20
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PPCPublisher.cs97
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs22
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishOptions.cs25
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteActions/IRemoteActionsService.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/DefaultRemoteAssistanceProvider.cs31
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/IRemoteAssistanceProvider.cs4
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs584
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs24
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/RemoteDesktopClient.cs20
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteJob/DefaultRemoteJobService.cs177
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteJob/IRemoteJobService.cs14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Colors.xaml11
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Merged.xaml10
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Styles.xaml65
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/finger3.pngbin0 -> 1005 bytes
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/tap.pngbin0 -> 1316 bytes
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/SQL/DefaultRemoteSqlService.cs59
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/SQL/IRemoteSqlService.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/SafetyLevelOperations.csv3
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/DefaultStorageProvider.cs23
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/IStorageProvider.cs6
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/DefaultMachineDataSynchronizer.cs662
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/IMachineDataSynchronizer.cs26
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationEndedEventArgs.cs15
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationState.cs23
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatus.cs47
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatusChangedEventArgs.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/DefaultSystemInfoService.cs138
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/ISystemInfoService.cs17
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj141
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/ThreadLoading/IThreadLoadingService.cs14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/AlternativeUnifiedWriteFilterManager.cs48
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/DefaultUnifiedWriteFilterManager.cs35
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/IUnifiedWriteFilterManager.cs3
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/DefaultPackageRunner.cs362
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/IPPCPackage.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/IPackageRunner.cs18
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PPCPackageAttribute.cs26
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageContext.cs28
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageProgressEventArgs.cs17
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageRunnerResult.cs13
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageStateChangedEventArgs.cs16
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackagesFile.cs19
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateRequest.cs16
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateResponse.cs10
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataRequest.cs24
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataResponse.cs27
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateRequest.cs2
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateResponse.cs6
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LatestVersionResponse.cs1
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LoginRequest.cs1
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupRequest.cs1
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupResponse.cs7
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineUpdateCompletedRequest.cs19
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineUpdateCompletedResponse.cs15
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedRequest.cs27
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedResponse.cs16
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs5
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs45
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/SynchronizationFailedEntity.cs14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs3
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBResponse.cs1
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdatedEntity.cs31
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataRequest.cs31
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataResponse.cs31
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/packages.config1
121 files changed, 8034 insertions, 376 deletions
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Application/IPPCApplicationManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Application/IPPCApplicationManager.cs
index 7c67ac1a3..a9d4e7c3a 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Application/IPPCApplicationManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Application/IPPCApplicationManager.cs
@@ -46,6 +46,11 @@ namespace Tango.PPC.Common.Application
event EventHandler SystemRestartRequired;
/// <summary>
+ /// Occurs when the updater utility has failed to perform the last update.
+ /// </summary>
+ event EventHandler UpdaterFailed;
+
+ /// <summary>
/// Occurs when the application has encountered an error when initializing.
/// </summary>
event EventHandler<Exception> ApplicationInitializationError;
@@ -61,6 +66,16 @@ namespace Tango.PPC.Common.Application
bool IsInTechnicianMode { get; }
/// <summary>
+ /// Gets a value indicating whether an update has occurred before the application started.
+ /// </summary>
+ bool IsAfterUpdate { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether the updater utility has failed to perform the last update.
+ /// </summary>
+ bool IsUpdateFailed { get; }
+
+ /// <summary>
/// Shutdown the application.
/// </summary>
void ShutDown();
@@ -91,6 +106,11 @@ namespace Tango.PPC.Common.Application
Version Version { get; }
/// <summary>
+ /// Gets the firmware version.
+ /// </summary>
+ Version FirmwareVersion { get; }
+
+ /// <summary>
/// Gets the application build date.
/// </summary>
String BuildDate { get; }
@@ -101,6 +121,11 @@ namespace Tango.PPC.Common.Application
DateTime StartUpDate { get; }
/// <summary>
+ /// Gets or sets the application folder.
+ /// </summary>
+ String StartPath { get; }
+
+ /// <summary>
/// Gets or sets a value indicating whether the screen is currently locked.
/// </summary>
bool IsScreenLocked { get; set; }
@@ -114,5 +139,11 @@ namespace Tango.PPC.Common.Application
/// Invokes a dialog for entering a password and releasing the screen lock.
/// </summary>
void ReleaseScreenLock();
+
+ /// <summary>
+ /// Sets the state of the main window.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ void SetWindowState(WindowState state);
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Authentication/IAuthenticationProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Authentication/IAuthenticationProvider.cs
index 33761c8d6..ca927e6df 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Authentication/IAuthenticationProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Authentication/IAuthenticationProvider.cs
@@ -23,6 +23,11 @@ namespace Tango.PPC.Common.Authentication
User CurrentUser { get; }
/// <summary>
+ /// Gets a value indicating whether the authentication provider is using a null user.
+ /// </summary>
+ bool AuthenticationRequired { get; }
+
+ /// <summary>
/// Performs a user login by the specified email and password.
/// </summary>
/// <param name="email">The email.</param>
@@ -32,6 +37,12 @@ namespace Tango.PPC.Common.Authentication
Task<User> Login(String email, String password, bool encrypt = true);
/// <summary>
+ /// Performs a fake login when no authentication is used.
+ /// </summary>
+ /// <returns></returns>
+ Task Login();
+
+ /// <summary>
/// Logs-out the current logged-in user.
/// </summary>
void LogOut();
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs
new file mode 100644
index 000000000..c687377a6
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs
@@ -0,0 +1,81 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.PMR.Exports;
+using Tango.Web;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ /// <summary>
+ /// Represents a backup file record.
+ /// </summary>
+ public class BackupFile
+ {
+ /// <summary>
+ /// Gets or sets the backup file version.
+ /// </summary>
+ public int Version { get; set; }
+
+ /// <summary>
+ /// Gets or sets the backup name.
+ /// </summary>
+ public String Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the creation date.
+ /// </summary>
+ public DateTime Date { get; set; }
+
+ /// <summary>
+ /// Gets or sets the machine serial number.
+ /// </summary>
+ public String MachineSerialNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the application version.
+ /// </summary>
+ public String ApplicationVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the firmware version.
+ /// </summary>
+ public String FirmwareVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the settings file.
+ /// </summary>
+ public String SettingsFile { get; set; }
+
+ /// <summary>
+ /// Gets or sets the job files.
+ /// </summary>
+ public List<JobFile> JobFiles { get; set; }
+
+ /// <summary>
+ /// Gets or sets the backup settings.
+ /// </summary>
+ public BackupSettings Settings { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BackupFile"/> class.
+ /// </summary>
+ public BackupFile()
+ {
+ Settings = new BackupSettings();
+ JobFiles = new List<JobFile>();
+ }
+
+ public String ToJson()
+ {
+ return JsonConvert.SerializeObject(this);
+ }
+
+ public static BackupFile FromJson(String json)
+ {
+ return JsonConvert.DeserializeObject<BackupFile>(json);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupMode.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupMode.cs
new file mode 100644
index 000000000..8533ce22a
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupMode.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ /// <summary>
+ /// Represents a backup mode.
+ /// </summary>
+ public enum BackupMode
+ {
+ /// <summary>
+ /// Jobs only backup.
+ /// </summary>
+ Jobs,
+ /// <summary>
+ /// Complete backup of data, app binaries, firmware and settings file.
+ /// </summary>
+ Full,
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreProgressEventArgs.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreProgressEventArgs.cs
new file mode 100644
index 000000000..d12db7b56
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreProgressEventArgs.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ /// <summary>
+ /// Represents a backup restore procedure progress.
+ /// </summary>
+ /// <seealso cref="System.EventArgs" />
+ public class BackupRestoreProgressEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether the progress is intermediate.
+ /// </summary>
+ public bool IsIntermediate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the progress value.
+ /// </summary>
+ public double Progress { get; set; }
+
+
+ /// <summary>
+ /// Gets or sets the maximum progress.
+ /// </summary>
+ public double MaxProgress { get; set; }
+
+ /// <summary>
+ /// Gets or sets the progress stage.
+ /// </summary>
+ public BackupRestoreStage Stage { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs
new file mode 100644
index 000000000..4e0398237
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ /// <summary>
+ /// Represents a backup restore procedure stage.
+ /// </summary>
+ public enum BackupRestoreStage
+ {
+ [Description("Initializing")]
+ Initializing,
+
+ //Backup
+ [Description("Backing up jobs...")]
+ BackingupJobs,
+ [Description("Backing up data...")]
+ BackingupDatabase,
+ [Description("Backing up application...")]
+ BackingupApplication,
+ [Description("Backing up user settings...")]
+ BackingupSettings,
+ [Description("Writing configuration...")]
+ WritingConfiguration,
+ [Description("Compressing files...")]
+ CompressingFiles,
+ [Description("Encrypting...")]
+ Encrypting,
+
+ //Restore
+ [Description("Decrypting...")]
+ Decrypting,
+ [Description("Extracting backup configuration...")]
+ ExtractingBackupConfiguration,
+ [Description("Validating machine state...")]
+ ValidatingMachineState,
+ [Description("Extracting content...")]
+ ExtractingContent,
+ [Description("Restoring user settings...")]
+ RestoringSettings,
+ [Description("Restoring jobs...")]
+ RestoringJobs,
+ [Description("Restoring data...")]
+ RestoringDatabase,
+ [Description("Removing temporary files...")]
+ RemovingTemporaryFiles,
+ [Description("Restoring firmware version...")]
+ RestoringFirmware,
+ [Description("Rolling back changes...")]
+ RollingBackChanges,
+
+
+ [Description("Done")]
+ Done,
+
+ [Description("Error")]
+ Error,
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupSettings.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupSettings.cs
new file mode 100644
index 000000000..b2021ba39
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupSettings.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ /// <summary>
+ /// Represents a backup settings.
+ /// </summary>
+ public class BackupSettings
+ {
+ /// <summary>
+ /// Gets or sets the backup mode.
+ /// </summary>
+ public BackupMode Mode { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs
new file mode 100644
index 000000000..d32df734d
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs
@@ -0,0 +1,604 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.PPC.Common.Application;
+using Tango.PPC.Common.BackupRestore;
+using Tango.Core.ExtensionMethods;
+using Tango.Core.DI;
+using Tango.PPC.Common.Connection;
+using System.IO;
+using Tango.Core.Helpers;
+using Tango.BL;
+using Tango.Settings;
+using Tango.Core.DB;
+using System.Data.SqlClient;
+using Ionic.Zip;
+using Tango.BL.Entities;
+using Tango.PPC.Common.Authentication;
+using Tango.Integration.Upgrade;
+using Tango.Core.IO;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ public class DefaultBackupManager : ExtendedObject, IBackupManager
+ {
+ private const string BACKUP_FILE_NAME = "Backup.json";
+ private const string DATABASE_FILE_NAME = "Tango.bak";
+ private const int VERSION = 1;
+ private const string PASSWORD = "1Creativity";
+
+ [TangoInject(TangoInjectMode.WhenAvailable)]
+ private IPPCApplicationManager _applicationManager;
+
+ [TangoInject(TangoInjectMode.WhenAvailable)]
+ private IMachineProvider _machineProvider;
+
+ [TangoInject(TangoInjectMode.WhenAvailable)]
+ private IAuthenticationProvider _authenticationProvider;
+
+ public DefaultBackupManager()
+ {
+ TangoIOC.Default.Inject(this);
+ }
+
+ public event EventHandler<BackupRestoreProgressEventArgs> Progress;
+
+ public Task CreateBackup(string filePath, String name, BackupSettings settings)
+ {
+ return Task.Factory.StartNew(() =>
+ {
+ var tempFolder = TemporaryManager.CreateFolder();
+
+ try
+ {
+ //Basic
+ LogManager.Log($"Starting backup operation to file '{filePath}'...");
+ LogManager.Log($"Backup settings:\n{settings.ToJsonString()}");
+ OnProgress(BackupRestoreStage.Initializing);
+
+ LogManager.Log($"Temporary folder created on {tempFolder.Path}.");
+
+ BackupFile backupFile = new BackupFile();
+ backupFile.Version = VERSION;
+ backupFile.Date = DateTime.Now;
+ backupFile.Settings = settings;
+ backupFile.Name = name;
+ backupFile.MachineSerialNumber = _machineProvider.Machine.SerialNumber;
+ backupFile.ApplicationVersion = _applicationManager.Version.ToString();
+
+ //Firmware
+ try
+ {
+ LogManager.Log("Extracting firmware version from local tfp package...");
+ using (var st = File.OpenRead(Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "firmware_package.tfp")))
+ {
+ backupFile.FirmwareVersion = _machineProvider.MachineOperator.GetFirmwarePackageInfo(st).Result.FileDescriptors.SingleOrDefault(x => x.Destination == PMR.FirmwareUpgrade.VersionFileDestination.Mcu).Version;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new FileLoadException("Could extract the firmware version from the TFP package.", ex);
+ }
+
+ LogManager.Log($"Backup file generated:\n{backupFile.ToJsonString()}");
+
+ if (settings.Mode == BackupMode.Jobs)
+ {
+ //Jobs
+ LogManager.Log("Starting jobs backup...");
+ OnProgress(BackupRestoreStage.BackingupJobs);
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ var jobs = db.Jobs.ToList();
+
+ foreach (var job in jobs)
+ {
+ try
+ {
+ LogManager.Log($"Backing up job '{job.Name}'...");
+ var jobFile = job.ToJobFile().Result;
+ backupFile.JobFiles.Add(jobFile);
+
+ OnProgress(BackupRestoreStage.BackingupJobs, jobs.IndexOf(job) + 1, jobs.Count, false);
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException($"Error extracting job {job.Name}.", ex);
+ }
+ }
+ }
+ LogManager.Log("Jobs backup completed.");
+ }
+ else
+ {
+ //User Settings
+ LogManager.Log("Backing up application settings...");
+ OnProgress(BackupRestoreStage.BackingupSettings);
+ backupFile.SettingsFile = File.ReadAllText(SettingsManager.Default.FilePath);
+
+ //Application Version
+ LogManager.Log("Backing up application files...");
+ OnProgress(BackupRestoreStage.BackingupApplication);
+
+ try
+ {
+ PathHelper.CopyDirectory(AssemblyHelper.GetCurrentAssemblyFolder(), tempFolder, true, (current, total) =>
+ {
+ OnProgress(BackupRestoreStage.BackingupApplication, current, total, false);
+ });
+ }
+ catch (Exception ex)
+ {
+ throw new IOException($"Error occurred while copying application files.", ex);
+ }
+
+ //Database
+ LogManager.Log("Backing up database...");
+ OnProgress(BackupRestoreStage.BackingupDatabase);
+ try
+ {
+ var dataSource = ObservablesContext.GetActualDataSource();
+ using (var dbManager = DbManager.FromDataSource(dataSource))
+ {
+ Directory.CreateDirectory("C:\\Backups");
+ var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}";
+ if (File.Exists(dbBackupFile))
+ {
+ File.Delete(dbBackupFile);
+ }
+ dbManager.Backup(dataSource.Catalog, dbBackupFile);
+ File.Move(dbBackupFile, Path.Combine(tempFolder, DATABASE_FILE_NAME));
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error creating database backup", ex);
+ }
+
+ LogManager.Log("Database backup completed.");
+ }
+
+ //Backup.json
+ try
+ {
+ OnProgress(BackupRestoreStage.WritingConfiguration);
+ var backupFilePath = Path.Combine(tempFolder, BACKUP_FILE_NAME);
+ LogManager.Log($"Writing backup configuration file '{backupFilePath}'...");
+ File.WriteAllText(backupFilePath, backupFile.ToJsonString());
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error writing backup configuration file.", ex);
+ }
+
+ //Compression
+ LogManager.Log($"Generating {filePath}...");
+ using (ZipFile zip = new ZipFile())
+ {
+ zip.Password = PASSWORD;
+ zip.AddDirectory(tempFolder);
+
+ zip.SaveProgress += (x, e) =>
+ {
+ if (e.EventType == ZipProgressEventType.Saving_AfterWriteEntry)
+ {
+ LogManager.Log($"Compressing '{e.CurrentEntry.FileName}'...");
+ OnProgress(BackupRestoreStage.CompressingFiles, e.EntriesSaved + 1, e.EntriesTotal, false);
+ }
+ };
+
+ zip.ParallelDeflateThreshold = -1;
+ zip.Save(filePath);
+ }
+
+ //Done
+ LogManager.Log("Backup operation completed!!!");
+ OnProgress(BackupRestoreStage.Done, 100, 100, false);
+ tempFolder.Delete();
+ }
+ catch (Exception ex)
+ {
+ tempFolder.Delete();
+
+ OnProgress(BackupRestoreStage.Error, 100, 100, false);
+ LogManager.Log(ex, "Could not complete the backup operation.");
+ throw ex;
+ }
+ });
+ }
+
+ /// <summary>
+ /// Extracts the backup configuration from the specified backup file.
+ /// </summary>
+ /// <param name="filePath">The file path.</param>
+ /// <returns></returns>
+ public Task<BackupFile> ExtractBackupConfiguration(string filePath)
+ {
+ return Task.Factory.StartNew<BackupFile>(() =>
+ {
+ using (ZipFile zip = ZipFile.Read(filePath))
+ {
+ zip.Password = PASSWORD;
+ var reader = zip.Entries.SingleOrDefault(x => x.FileName == BACKUP_FILE_NAME).OpenReader();
+ String json = String.Empty;
+
+ using (StreamReader stReader = new StreamReader(reader))
+ {
+ json = stReader.ReadToEnd();
+ }
+
+ var backupFile = BackupFile.FromJson(json);
+ reader.Close();
+ reader.Dispose();
+ return backupFile;
+ }
+ });
+ }
+
+ public Task<RestoreResult> Restore(string filePath, RestoreSettings settings)
+ {
+ TaskCompletionSource<RestoreResult> completionSource = new TaskCompletionSource<RestoreResult>();
+
+ String dbRollbackFile = null;
+ bool shouldRollback = false;
+
+ Task.Factory.StartNew(() =>
+ {
+ LogManager.Log($"Starting restore operation from file '{filePath}'...");
+ OnProgress(BackupRestoreStage.Initializing);
+
+ var tempFolder = TemporaryManager.CreateFolder();
+ tempFolder.Persist = true;
+
+ var restoreResult = new RestoreResult() { FolderPath = tempFolder };
+
+ try
+ {
+ LogManager.Log("Extracting backup file configuration...");
+
+ BackupFile backupFile = null;
+
+ //Extract Configuration
+ try
+ {
+ OnProgress(BackupRestoreStage.ExtractingBackupConfiguration);
+ backupFile = ExtractBackupConfiguration(filePath).Result;
+ restoreResult.BackupFile = backupFile;
+ LogManager.Log($"Backup settings:\n{backupFile.Settings.ToJsonString()}");
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error extracting backup configuration.", ex);
+ }
+
+ //Validate Version
+ if (backupFile.Version > VERSION)
+ {
+ throw new NotSupportedException($"Backup file version {backupFile} is not supported.");
+ }
+
+ //Validate Machine Serial Number
+ if (backupFile.Settings.Mode == BackupMode.Full && backupFile.MachineSerialNumber != _machineProvider.Machine.SerialNumber)
+ {
+ throw new InvalidOperationException($"The specified backup file targets machine '{backupFile.MachineSerialNumber}'. Cannot perform the restore operation.");
+ }
+
+ //Validate Machine State
+ LogManager.Log("Validating machine state...");
+ OnProgress(BackupRestoreStage.ValidatingMachineState);
+ if (_machineProvider.MachineOperator.IsPrinting)
+ {
+ LogManager.Log("The machine is currently printing. Aborting!");
+ throw new InvalidOperationException("Cannot perform restore operation while machine is dyeing.");
+ }
+
+ if (backupFile.Settings.Mode == BackupMode.Full && _machineProvider.MachineOperator.State != Transport.TransportComponentState.Connected)
+ {
+ LogManager.Log("Backup is configured to restore the firmware but machine is not connected!");
+ throw new InvalidOperationException("The restore operation is configured to restore the firmware version but the machine is currently disconnected.");
+ }
+
+ //Create Restore Point
+ try
+ {
+ LogManager.Log("Creating database rollback file...");
+ var dataSource = ObservablesContext.GetActualDataSource();
+ using (var dbManager = DbManager.FromDataSource(dataSource))
+ {
+ Directory.CreateDirectory("C:\\Backups");
+ dbRollbackFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak";
+ LogManager.Log($"Creating database rollback to '{dbRollbackFile}'...");
+ dbManager.Backup(dataSource.Catalog, dbRollbackFile);
+ LogManager.Log("Database rollback created successfully.");
+ shouldRollback = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidDataException("Error creating database rollback file.", ex);
+ }
+
+ if (backupFile.Settings.Mode == BackupMode.Jobs)
+ {
+ //Restore Jobs
+ OnProgress(BackupRestoreStage.RestoringJobs);
+ LogManager.Log("Starting jobs restore...");
+
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ var jobs = db.Jobs.ToList();
+ var jobFiles = backupFile.JobFiles;
+
+ if (settings.AllowDeleteJobs)
+ {
+ try
+ {
+ LogManager.Log("Removing existing jobs...");
+ foreach (var job in jobs.ToList())
+ {
+ LogManager.Log($"Removing job '{job.Name}'...");
+ job.Delete(db);
+ jobs.Remove(job);
+ }
+
+ db.SaveChanges();
+ }
+ catch (Exception ex)
+ {
+ throw new Exception("Error removing existing jobs from database.", ex);
+ }
+ }
+
+ foreach (var jobFile in jobFiles)
+ {
+ LogManager.Log($"Importing job '{jobFile.Name}'...");
+
+ try
+ {
+ var existingJob = jobs.FirstOrDefault(x => x.Name == jobFile.Name);
+
+ if (existingJob != null)
+ {
+ if (settings.OverwriteExistingJobs)
+ {
+ try
+ {
+ LogManager.Log("Job already exist, overwriting...");
+
+ var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, null).Result;
+ newJob.Guid = existingJob.Guid;
+
+ existingJob.Delete(db);
+ jobs.Remove(existingJob);
+
+ db.SaveChanges();
+ db.Jobs.Add(newJob);
+ db.SaveChanges();
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException("Error overwriting job.", ex);
+ }
+ }
+ }
+ else
+ {
+ var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, null).Result;
+ db.Jobs.Add(newJob);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException("Error importing job.", ex);
+ }
+
+ OnProgress(BackupRestoreStage.RestoringJobs, jobFiles.IndexOf(jobFile) + 1, jobFiles.Count, false);
+ }
+
+ OnProgress(BackupRestoreStage.RestoringJobs);
+ db.SaveChanges();
+ }
+
+ LogManager.Log("Jobs restored successfully.");
+ OnProgress(BackupRestoreStage.Done, 100, 100, false);
+ completionSource.SetResult(restoreResult);
+ }
+ else
+ {
+ //Extract zip file
+ LogManager.Log("Starting backup file extraction...");
+ OnProgress(BackupRestoreStage.ExtractingContent);
+ try
+ {
+ using (ZipFile zip = new ZipFile(filePath))
+ {
+ zip.Password = PASSWORD;
+
+ zip.ExtractProgress += (x, e) =>
+ {
+ if (e.EventType == ZipProgressEventType.Extracting_AfterExtractEntry)
+ {
+ LogManager.Log($"Extracting '{e.CurrentEntry.FileName}'...");
+ OnProgress(BackupRestoreStage.ExtractingContent, e.EntriesExtracted + 1, e.EntriesTotal, false);
+ }
+ };
+
+ zip.ParallelDeflateThreshold = -1;
+ zip.ExtractAll(tempFolder);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error extracting backup content.", ex);
+ }
+
+ //Overwrite settings
+ LogManager.Log("Validating user settings...");
+ if (backupFile.SettingsFile != null)
+ {
+ try
+ {
+ LogManager.Log("Overwriting settings file...");
+ OnProgress(BackupRestoreStage.RestoringSettings);
+ File.WriteAllText(SettingsManager.Default.FilePath, backupFile.SettingsFile);
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error overwriting user settings.", ex);
+ }
+ }
+ else
+ {
+ LogManager.Log("No user settings, skipping...");
+ }
+
+ //Restore database
+ var backupFilePath = Path.Combine(tempFolder, DATABASE_FILE_NAME);
+ LogManager.Log($"Looking for file database backup on '{backupFilePath}'...");
+ if (File.Exists(backupFilePath))
+ {
+ LogManager.Log("Restoring database...");
+ OnProgress(BackupRestoreStage.RestoringDatabase);
+ try
+ {
+ var dataSource = ObservablesContext.GetActualDataSource();
+ using (var dbManager = DbManager.FromDataSource(dataSource))
+ {
+ Directory.CreateDirectory("C:\\Backups");
+ var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}";
+ File.Copy(backupFilePath, dbBackupFile, true);
+ dbManager.Restore(dataSource.Catalog, dbBackupFile);
+ File.Delete(dbBackupFile);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error restoring database backup", ex);
+ }
+
+ LogManager.Log("Database backup completed.");
+ }
+ else
+ {
+ LogManager.Log("Database backup file not found, skipping...");
+ }
+
+ //Remove extra files from application temp folder
+ OnProgress(BackupRestoreStage.RemovingTemporaryFiles);
+ LogManager.Log("Removing redundant files from temp folder...");
+ try
+ {
+ File.Delete(backupFilePath);
+ }
+ catch { }
+ try
+ {
+ File.Delete(Path.Combine(tempFolder, BACKUP_FILE_NAME));
+ }
+ catch { }
+
+ //Update firmware
+ var tfpFile = Path.Combine(tempFolder, "firmware_package.tfp");
+ LogManager.Log($"Looking for tfp file on '{tfpFile}'...");
+ if (File.Exists(tfpFile))
+ {
+ OnProgress(BackupRestoreStage.RestoringFirmware);
+ LogManager.Log("Restoring firmware version...");
+
+ var stream = new FileStream(tfpFile, FileMode.Open);
+
+ _machineProvider.MachineOperator.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
+
+
+ var handler = _machineProvider.MachineOperator.UpgradeFirmware(stream, _machineProvider.Machine.IsDemo).Result;
+ handler.Failed += (_, ex) =>
+ {
+ stream.Dispose();
+ OnRestoreException(ex, completionSource, shouldRollback, dbRollbackFile, tempFolder);
+ };
+ handler.Completed += (_, __) =>
+ {
+ OnProgress(BackupRestoreStage.RestoringFirmware, 100, 100, false);
+ stream.Dispose();
+ LogManager.Log("Full backup restored successfully.");
+ OnProgress(BackupRestoreStage.Done, 100, 100, false);
+ completionSource.SetResult(restoreResult);
+ };
+ handler.Canceled += (_, __) =>
+ {
+ stream.Dispose();
+ OnRestoreException(new Exception("The operation has been canceled."), completionSource, shouldRollback, dbRollbackFile, tempFolder);
+ };
+ handler.Progress += (_, e) =>
+ {
+ OnProgress(BackupRestoreStage.RestoringFirmware, e.Current, e.Total, false);
+ };
+ }
+ else
+ {
+ LogManager.Log("Firmware package file not found, skipping...");
+ OnProgress(BackupRestoreStage.Done, 100, 100, false);
+ completionSource.SetResult(restoreResult);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ OnRestoreException(ex, completionSource, shouldRollback, dbRollbackFile, tempFolder);
+ }
+ });
+
+ return completionSource.Task;
+ }
+
+ private void OnRestoreException(Exception ex, TaskCompletionSource<RestoreResult> completionSource, bool shouldRollback, String dbRollbackFile, TemporaryFolder tempFolder)
+ {
+ if (shouldRollback)
+ {
+ LogManager.Log("Rolling back database changes...");
+
+ var dataSource = ObservablesContext.GetActualDataSource();
+ using (var dbManager = DbManager.FromDataSource(dataSource))
+ {
+ try
+ {
+ OnProgress(BackupRestoreStage.RollingBackChanges);
+ dbManager.Restore(dataSource.Catalog, dbRollbackFile);
+ LogManager.Log("Database restored successfully.");
+ }
+ catch (Exception e)
+ {
+ LogManager.Log(e, "Error rolling back database.");
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(dbRollbackFile);
+ }
+ catch { }
+ }
+ }
+ }
+
+ tempFolder.Delete();
+ OnProgress(BackupRestoreStage.Error, 100, 100, false);
+ LogManager.Log(ex, "Could not complete the restore operation.");
+ completionSource.SetException(ex);
+ }
+
+ protected virtual void OnProgress(BackupRestoreStage stage, double progress = 0, double maxProgress = 100, bool isIntermediate = true)
+ {
+ Progress?.Invoke(this, new BackupRestoreProgressEventArgs()
+ {
+ Stage = stage,
+ Progress = progress,
+ MaxProgress = maxProgress,
+ IsIntermediate = isIntermediate,
+ });
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs
new file mode 100644
index 000000000..ae1884677
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ /// <summary>
+ /// Represents a backup/restore manager.
+ /// </summary>
+ public interface IBackupManager
+ {
+ /// <summary>
+ /// Occurs when the backup/restore procedure makes progress.
+ /// </summary>
+ event EventHandler<BackupRestoreProgressEventArgs> Progress;
+
+ /// <summary>
+ /// Creates a backup file containing database, application and firmware versions.
+ /// </summary>
+ /// <param name="filePath">The file path.</param>
+ /// <param name="name">The backup name.</param>
+ /// <param name="settings">Backup configuration.</param>
+ /// <returns></returns>
+ Task CreateBackup(String filePath, String name, BackupSettings settings);
+
+ /// <summary>
+ /// Restores a backup located in the specified file path.
+ /// </summary>
+ /// <param name="filePath">The file path.</param>
+ /// <param name="settings">The restore settings</param>
+ /// <returns></returns>
+ Task<RestoreResult> Restore(String filePath, RestoreSettings settings);
+
+ /// <summary>
+ /// Extracts the backup configuration from the specified backup file.
+ /// </summary>
+ /// <param name="filePath">The file path.</param>
+ /// <returns></returns>
+ Task<BackupFile> ExtractBackupConfiguration(String filePath);
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs
new file mode 100644
index 000000000..5f10aebcf
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ public class RestoreResult
+ {
+ public String FolderPath { get; set; }
+ public BackupFile BackupFile { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs
new file mode 100644
index 000000000..a5b343302
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+
+namespace Tango.PPC.Common.BackupRestore
+{
+ public class RestoreSettings : ExtendedObject
+ {
+ private bool _allowDeleteJobs;
+ public bool AllowDeleteJobs
+ {
+ get { return _allowDeleteJobs; }
+ set { _allowDeleteJobs = value; RaisePropertyChangedAuto(); }
+ }
+
+ private bool _overwriteExistingJobs;
+ public bool OverwriteExistingJobs
+ {
+ get { return _overwriteExistingJobs; }
+ set { _overwriteExistingJobs = value; RaisePropertyChangedAuto(); }
+ }
+
+ public RestoreSettings()
+ {
+ OverwriteExistingJobs = true;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs
index a16e2f649..b2c752ca8 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs
@@ -21,6 +21,7 @@ using Tango.Integration;
using Tango.Transport;
using System.Threading;
using Tango.Core.ExtensionMethods;
+using System.IO.Ports;
namespace Tango.PPC.Common.Connection
{
@@ -35,6 +36,26 @@ namespace Tango.PPC.Common.Connection
private Thread _connection_thread;
private ObservablesContext _context;
+ /// <summary>
+ /// Occurs when the machine has connected.
+ /// </summary>
+ public event EventHandler MachineConnected;
+
+ /// <summary>
+ /// Occurs when the machine has disconnected.
+ /// </summary>
+ public event EventHandler MachineDisconnected;
+
+ private bool _isConnected;
+ /// <summary>
+ /// Gets a value indicating whether the machine is currently connected.
+ /// </summary>
+ public bool IsConnected
+ {
+ get { return _isConnected; }
+ private set { _isConnected = value; RaisePropertyChangedAuto(); }
+ }
+
private Machine _machine;
/// <summary>
/// Gets the database machine entity associated with the current machine.
@@ -74,18 +95,24 @@ namespace Tango.PPC.Common.Connection
/// </summary>
public DefaultMachineProvider()
{
+ var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+
MachineOperator = new MachineOperator();
+ MachineOperator.StatusChanged += MachineOperator_StatusChanged;
MachineOperator.EnableEventsNotification = true;
MachineOperator.EnableJobResume = true;
MachineOperator.UseKeepAlive = true;
MachineOperator.EnableMachineStatusUpdates = true;
- MachineOperator.EnableDiagnostics = false;
- MachineOperator.EnableEmbeddedDebugging = false;
- MachineOperator.FirmwareUpgradeMode = Integration.Upgrade.FirmwareUpgradeModes.DFU | Integration.Upgrade.FirmwareUpgradeModes.TFP_PACKAGE;
+ MachineOperator.EnableDiagnostics = true;
+ MachineOperator.EnablePowerUpSequence = true;
+ MachineOperator.EnableEmbeddedDebugging = settings.EnableEmbeddedDebugLogs;
+ MachineOperator.EnableAutomaticThreadLoading = settings.EnableAutomaticThreadLoading;
+ MachineOperator.JobRunsLogger.JobSource = BL.Enumerations.JobSource.Local;
- var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+ MachineOperator.FirmwareUpgradeMode = Integration.Upgrade.FirmwareUpgradeModes.DFU | Integration.Upgrade.FirmwareUpgradeModes.TFP_PACKAGE;
MachineOperator.JobUploadStrategy = settings.JobUploadStrategy;
+ MachineOperator.JobUnitsMethod = settings.JobUnitsMethod;
MachineOperator.GradientGenerationConfiguration.IsEnabled = settings.EnableGradientGeneration;
MachineOperator.GradientGenerationConfiguration.ResolutionCM = settings.GradientGenerationResolution;
@@ -96,8 +123,28 @@ namespace Tango.PPC.Common.Connection
MachineOperator.EnableJobLiquidQuantityValidation = settings.EnableJobLiquidQuantityValidation;
}
+ private void MachineOperator_StatusChanged(object sender, MachineStatuses status)
+ {
+ if (status != MachineStatuses.Disconnected)
+ {
+ if (!IsConnected)
+ {
+ OnMachineConnected();
+ }
+ }
+ else
+ {
+ if (IsConnected)
+ {
+ OnMachineDisconnected();
+ }
+ }
+ }
+
private async void ConnectionThreadMethod()
{
+ bool fileLoggingDisabled = false;
+
while (true)
{
if (MachineOperator.State != TransportComponentState.Connected)
@@ -106,7 +153,10 @@ namespace Tango.PPC.Common.Connection
{
Thread.Sleep(2000);
- LogManager.Log("Starting machine connection procedure...", LogCategory.Debug);
+ if (!fileLoggingDisabled)
+ {
+ LogManager.Log("Starting machine connection procedure...", LogCategory.Info);
+ }
var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
@@ -116,22 +166,28 @@ namespace Tango.PPC.Common.Connection
{
TimeSpan timeout = TimeSpan.FromSeconds(SettingsManager.Default.GetOrCreate<PPCSettings>().MachineScanningTimeoutSeconds);
- LogManager.Log("Scanning for machine on available serial ports...", LogCategory.Debug);
+ LogManager.Log("Scanning for machine on available serial ports...", LogCategory.Info);
Transport.Discovery.UsbCommunicationScanner<ConnectRequest, ConnectResponse> scanner = new Transport.Discovery.UsbCommunicationScanner<ConnectRequest, ConnectResponse>(UsbSerialBaudRates.BR_115200);
var response = await scanner.Scan(new ConnectRequest() { Password = "1234" }, settings.EmbeddedDeviceHint, timeout);
- LogManager.Log("Machine discovered on port: " + response.Adapter.Address, LogCategory.Debug);
+ LogManager.Log("Machine discovered on port: " + response.Adapter.Address, LogCategory.Info);
LogManager.Log("Device Information:", LogCategory.Debug);
- LogManager.Log(response.Response.DeviceInformation.ToJsonString(), LogCategory.Debug);
+ LogManager.Log(response.Response.DeviceInformation.ToJsonString(), LogCategory.Info);
- LogManager.Log("Disconnecting machine operator...", LogCategory.Debug);
+ LogManager.Log("Disconnecting machine operator...", LogCategory.Info);
await MachineOperator.Disconnect();
MachineOperator.Adapter = response.Adapter;
MachineOperator.JobHandlingMode = JobHandlerModes.SettingUp;
- LogManager.Log("Connecting machine operator...", LogCategory.Debug);
+ LogManager.Log("Connecting machine operator...", LogCategory.Info);
try
{
await MachineOperator.Connect();
+
+ if (MachineOperator.DeviceInformation != null)
+ {
+ settings.FirmwareVersion = MachineOperator.DeviceInformation.Version;
+ settings.Save();
+ }
}
catch (Exception)
{
@@ -146,7 +202,17 @@ namespace Tango.PPC.Common.Connection
}
else
{
- LogManager.Log($"Connecting to machine on {settings.EmbeddedComPort}...", LogCategory.Debug);
+ //Perform a pre-test to not overload the log file when machine is off for a long time.
+ using (SerialPort preCheckSerialPort = new SerialPort(settings.EmbeddedComPort))
+ {
+ preCheckSerialPort.BaudRate = UsbSerialBaudRates.BR_115200.ToInt32();
+ preCheckSerialPort.Open();
+ preCheckSerialPort.Close();
+ fileLoggingDisabled = false;
+ Thread.Sleep(500); //Wait a little while to not scare the other side?..
+ }
+
+ LogManager.Log($"Connecting to machine on {settings.EmbeddedComPort}...", LogCategory.Info);
UsbTransportAdapter adapter = new UsbTransportAdapter(settings.EmbeddedComPort, UsbSerialBaudRates.BR_115200);
MachineOperator.Adapter = adapter;
@@ -154,6 +220,12 @@ namespace Tango.PPC.Common.Connection
try
{
await MachineOperator.Connect();
+
+ if (MachineOperator.DeviceInformation != null)
+ {
+ settings.FirmwareVersion = MachineOperator.DeviceInformation.Version;
+ settings.Save();
+ }
}
catch (Exception)
{
@@ -184,13 +256,24 @@ namespace Tango.PPC.Common.Connection
LogManager.Log("Connecting machine operator...");
await MachineOperator.Connect();
+ if (MachineOperator.DeviceInformation != null)
+ {
+ settings.FirmwareVersion = MachineOperator.DeviceInformation.Version;
+ settings.Save();
+ }
+
await Task.Delay(1000);
await MachineOperator.UploadHardwareConfiguration(Machine.Configuration.HardwareVersion, Machine.Configuration);
}
}
catch (Exception ex)
{
- LogManager.Log(ex, LogCategory.Debug, "Error while trying to scan and connect to the machine.");
+ if (!fileLoggingDisabled || LogManager.Categories.Contains(LogCategory.Debug))
+ {
+ LogManager.Log(ex, "Error while trying to scan and connect to the machine.");
+ LogManager.Log("Application logging of further connection attempts is now disabled and will resume when connection is successful.");
+ fileLoggingDisabled = true;
+ }
}
}
@@ -217,6 +300,15 @@ namespace Tango.PPC.Common.Connection
if (Machine != null)
{
LogManager.Log("First machine entry found. Machine serial number is: " + Machine.SerialNumber + ".");
+
+ if (Machine.IsDemo)
+ {
+ LogManager.Log("Machine is in demo mode. Changing firmware upgrade mode to TFP package only.");
+ MachineOperator.FirmwareUpgradeMode = Integration.Upgrade.FirmwareUpgradeModes.TFP_PACKAGE;
+ }
+
+ MachineOperator.JobRunsLogger.SetDefaultMachine(Machine);
+
ConnectToMachine();
}
else
@@ -247,7 +339,7 @@ namespace Tango.PPC.Common.Connection
public async Task SaveMachine()
{
await _context.SaveChangesAsync();
- Machine = await new MachineBuilder(_context).SetFirst().WithSettings().BuildAsync();
+ Machine = await new MachineBuilder(_context).SetFirst().BuildAsync();
TangoMessenger.Default.Send(new MachineSettingsSavedMessage() { Machine = Machine });
}
@@ -304,5 +396,23 @@ namespace Tango.PPC.Common.Connection
return machineOperator;
}
+
+ /// <summary>
+ /// Called when the machine has connected.
+ /// </summary>
+ protected virtual void OnMachineConnected()
+ {
+ IsConnected = true;
+ MachineConnected?.Invoke(this, new EventArgs());
+ }
+
+ /// <summary>
+ /// Called when the machine has disconnected.
+ /// </summary>
+ protected virtual void OnMachineDisconnected()
+ {
+ IsConnected = false;
+ MachineDisconnected?.Invoke(this, new EventArgs());
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/IMachineProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/IMachineProvider.cs
index 10180b9cc..774fa7c9e 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/IMachineProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/IMachineProvider.cs
@@ -16,6 +16,21 @@ namespace Tango.PPC.Common.Connection
public interface IMachineProvider
{
/// <summary>
+ /// Occurs when the machine has connected.
+ /// </summary>
+ event EventHandler MachineConnected;
+
+ /// <summary>
+ /// Occurs when the machine has disconnected.
+ /// </summary>
+ event EventHandler MachineDisconnected;
+
+ /// <summary>
+ /// Gets a value indicating whether the machine is currently connected.
+ /// </summary>
+ bool IsConnected { get; }
+
+ /// <summary>
/// Gets the database machine entity associated with the current machine.
/// </summary>
Machine Machine { get; }
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connectivity/IConnectivityProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connectivity/IConnectivityProvider.cs
index 67b73d4f6..39404934d 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connectivity/IConnectivityProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connectivity/IConnectivityProvider.cs
@@ -20,11 +20,16 @@ namespace Tango.PPC.Common.Connectivity
event EventHandler<ConnectionStateEventArgs> ConnectionStateChanged;
/// <summary>
- /// Gets a value indicating whether there is any Internet connection available.
+ /// Gets a value indicating whether there is a WiFi connection.
/// </summary>
bool IsConnected { get; }
/// <summary>
+ /// Gets a value indicating whether there is LAN connection.
+ /// </summary>
+ bool IsLanConnected { get; }
+
+ /// <summary>
/// Gets the available WiFi networks.
/// </summary>
ObservableCollection<WiFiNetwork> AvailableWiFiNetworks { get; }
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Console/DefaultConsoleEngineService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Console/DefaultConsoleEngineService.cs
new file mode 100644
index 000000000..94b677b18
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Console/DefaultConsoleEngineService.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Console;
+using Tango.Console.Network;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Integration.ExternalBridge;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.Transport;
+
+namespace Tango.PPC.Common.Console
+{
+ /// <summary>
+ /// Represents the <see cref="IConsoleEngineService"/> default implementation
+ /// which listens to incoming console request by registering as a external bridge request handler.
+ /// </summary>
+ /// <seealso cref="Tango.PPC.Common.Console.IConsoleEngineService" />
+ /// <seealso cref="Tango.Integration.ExternalBridge.IExternalBridgeRequestHandler" />
+ [TangoCreateWhenRegistered]
+ public class DefaultConsoleEngineService : ExtendedObject, IConsoleEngineService, IExternalBridgeRequestHandler
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether this <see cref="IConsoleEngineService" /> is enabled.
+ /// </summary>
+ public bool Enabled { get; set; } = true;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultConsoleEngineService"/> class.
+ /// </summary>
+ /// <param name="externalBridge">The external bridge service instance.</param>
+ public DefaultConsoleEngineService(IPPCExternalBridgeService externalBridge)
+ {
+ externalBridge.RegisterRequestHandler(this);
+ }
+
+ /// <summary>
+ /// Handles <see cref="GetCurrentDirectoryRequest"/> requests.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="token">The token.</param>
+ /// <param name="transporter">The transporter.</param>
+ [ExternalBridgeRequestHandlerMethod(typeof(GetCurrentDirectoryRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnGetCurrentDirectoryRequest(GetCurrentDirectoryRequest request, String token, ITransporter transporter)
+ {
+ this.ThrowIfDisabled();
+
+ await transporter.SendGenericResponse(new GetCurrentDirectoryResponse()
+ {
+ CurrentDirectory = Environment.CurrentDirectory,
+ Suggestions = ConsoleExecutionEngine.GetSuggestions(Environment.CurrentDirectory)
+ }, token);
+ }
+
+ /// <summary>
+ /// Handles <see cref="ConsoleCommandRequest"/> requests.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="token">The token.</param>
+ /// <param name="transporter">The transporter.</param>
+ [ExternalBridgeRequestHandlerMethod(typeof(ConsoleCommandRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnConsoleCommandRequest(ConsoleCommandRequest request, String token, ITransporter transporter)
+ {
+ this.ThrowIfDisabled();
+
+ LogManager.Log($"{nameof(ConsoleCommandRequest)} received with command '{request.Command}'. Executing...");
+
+ ConsoleExecutionEngine engine = new ConsoleExecutionEngine();
+ var result = await engine.Execute(request);
+
+ LogManager.Log("Console command executed successfully.");
+
+ await transporter.SendGenericResponse<ConsoleCommandResponse>(new ConsoleCommandResponse()
+ {
+ Output = result.Output,
+ Suggestions = result.Suggestions,
+ WorkingFolder = result.WorkingFolder
+ }, token);
+ }
+
+ /// <summary>
+ /// Called when any of the external bridge clients (receivers) has disconnected.
+ /// </summary>
+ /// <param name="receiver">The receiver.</param>
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ //Do nothing.
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Console/IConsoleEngineService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Console/IConsoleEngineService.cs
new file mode 100644
index 000000000..18edb3629
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Console/IConsoleEngineService.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.Console
+{
+ /// <summary>
+ /// Represents a command prompt console service which listens for incoming console requests.
+ /// </summary>
+ public interface IConsoleEngineService : IPPCService
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Controls/ImageGalleryControl.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Controls/ImageGalleryControl.cs
new file mode 100644
index 000000000..ba2550e25
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Controls/ImageGalleryControl.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Markup;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+using Tango.SharedUI.Controls;
+
+namespace Tango.PPC.Common.Controls
+{
+ [ContentProperty(nameof(Elements))]
+ public class ImageGalleryControl : Control
+ {
+ private NavigationControl _navigationControl;
+ private DispatcherTimer _timer;
+
+ public int SelectedIndex
+ {
+ get { return (int)GetValue(SelectedIndexProperty); }
+ set { SetValue(SelectedIndexProperty, value); }
+ }
+ public static readonly DependencyProperty SelectedIndexProperty =
+ DependencyProperty.Register("SelectedIndex", typeof(int), typeof(ImageGalleryControl), new PropertyMetadata(0));
+
+ public ObservableCollection<FrameworkElement> Elements
+ {
+ get { return (ObservableCollection<FrameworkElement>)GetValue(ElementsProperty); }
+ set { SetValue(ElementsProperty, value); }
+ }
+ public static readonly DependencyProperty ElementsProperty =
+ DependencyProperty.Register("Elements", typeof(ObservableCollection<FrameworkElement>), typeof(ImageGalleryControl), new PropertyMetadata(null));
+
+ public Duration Duration
+ {
+ get { return (Duration)GetValue(DurationProperty); }
+ set { SetValue(DurationProperty, value); }
+ }
+ public static readonly DependencyProperty DurationProperty =
+ DependencyProperty.Register("Duration", typeof(Duration), typeof(ImageGalleryControl), new PropertyMetadata(new Duration(TimeSpan.FromSeconds(2))));
+
+
+ static ImageGalleryControl()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageGalleryControl), new FrameworkPropertyMetadata(typeof(ImageGalleryControl)));
+ }
+
+ public ImageGalleryControl()
+ {
+ Elements = new ObservableCollection<FrameworkElement>();
+ Loaded += ImageGalleryControl_Loaded;
+
+ _timer = new DispatcherTimer();
+ _timer.Tick += _timer_Tick;
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ _navigationControl = GetTemplateChild("navigationControl") as NavigationControl;
+ }
+
+ private void ImageGalleryControl_Loaded(object sender, RoutedEventArgs e)
+ {
+ if (_navigationControl != null)
+ {
+ _navigationControl.Elements = Elements;
+
+ _timer.Interval = Duration.TimeSpan;
+
+ if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
+ {
+ _timer.Start();
+ }
+ }
+ }
+
+ private void _timer_Tick(object sender, EventArgs e)
+ {
+ if (SelectedIndex < Elements.Count - 1)
+ {
+ SelectedIndex++;
+ }
+ else
+ {
+ SelectedIndex = 0;
+ }
+ }
+
+ protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
+ {
+ base.OnPreviewMouseDown(e);
+ _timer.Stop();
+ }
+
+ protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
+ {
+ base.OnPreviewMouseUp(e);
+ _timer.Start();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Controls/ImageGalleryControl.xaml b/Software/Visual_Studio/PPC/Tango.PPC.Common/Controls/ImageGalleryControl.xaml
new file mode 100644
index 000000000..495335ff1
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Controls/ImageGalleryControl.xaml
@@ -0,0 +1,101 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI"
+ xmlns:local="clr-namespace:Tango.PPC.Common.Controls">
+
+ <Style TargetType="{x:Type ListBoxItem}" x:Key="Gallery_BlankListBoxItem">
+ <Setter Property="Background" Value="Transparent"/>
+ <Setter Property="BorderThickness" Value="0"></Setter>
+ <Setter Property="Foreground" Value="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource AncestorType=ContentPresenter}}"></Setter>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
+ <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
+ <Setter Property="Padding" Value="0"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ListBoxItem}">
+ <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0" Background="{TemplateBinding Background}" Padding="0" SnapsToDevicePixels="true">
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsSelected" Value="true">
+ <Setter Property="Background" TargetName="Bd" Value="Transparent"/>
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsSelected" Value="true"/>
+ <Condition Property="Selector.IsSelectionActive" Value="false"/>
+ </MultiTrigger.Conditions>
+ <Setter Property="Background" TargetName="Bd" Value="Transparent"/>
+ </MultiTrigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style TargetType="{x:Type ListBox}" x:Key="Gallery_BlankListBox">
+ <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"></Setter>
+ <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"></Setter>
+ <Setter Property="BorderThickness" Value="0"></Setter>
+ <Setter Property="Background" Value="Transparent"></Setter>
+ <Setter Property="ItemContainerStyle" Value="{StaticResource Gallery_BlankListBoxItem}"></Setter>
+ </Style>
+
+ <Style TargetType="{x:Type local:ImageGalleryControl}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type local:ImageGalleryControl}">
+ <Border Background="{TemplateBinding Background}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}">
+
+ <DockPanel>
+
+ <ListBox DockPanel.Dock="Bottom" ItemsSource="{TemplateBinding Elements}" SelectedIndex="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=SelectedIndex,Mode=TwoWay}" Style="{StaticResource Gallery_BlankListBox}">
+ <ListBox.ItemsPanel>
+ <ItemsPanelTemplate>
+ <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"></StackPanel>
+ </ItemsPanelTemplate>
+ </ListBox.ItemsPanel>
+ <ListBox.ItemContainerStyle>
+ <Style TargetType="ListBoxItem" BasedOn="{StaticResource Gallery_BlankListBoxItem}">
+ <Setter Property="Margin" Value="10 0"></Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="ListBoxItem">
+ <Ellipse x:Name="ellipse" Stroke="{StaticResource TangoPrimaryAccentBrush}" Width="20" Height="20" StrokeThickness="1">
+ <Ellipse.Style>
+ <Style TargetType="Ellipse">
+
+ </Style>
+ </Ellipse.Style>
+ </Ellipse>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter TargetName="ellipse" Property="Fill" Value="{StaticResource TangoGrayBrush}"></Setter>
+ </Trigger>
+ <Trigger Property="IsSelected" Value="False">
+ <Setter TargetName="ellipse" Property="Fill" Value="Transparent"></Setter>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ </ListBox.ItemContainerStyle>
+ </ListBox>
+
+ <controls:NavigationControl x:Name="navigationControl" GalleryMode="True" TransitionType="Slide" TransitionDuration="00:00:0.2" SelectedIndex="{TemplateBinding SelectedIndex}">
+
+ </controls:NavigationControl>
+ </DockPanel>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/DataStore/DefaultDataStoreService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/DataStore/DefaultDataStoreService.cs
new file mode 100644
index 000000000..02539aed9
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/DataStore/DefaultDataStoreService.cs
@@ -0,0 +1,492 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core.DI;
+using Tango.DataStore;
+using Tango.DataStore.EF;
+using Tango.DataStore.Lite;
+using Tango.DataStore.Remote;
+using Tango.Integration.ExternalBridge;
+using Tango.PMR.DataStore;
+using Tango.PPC.Common.Connection;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.Transport;
+using Tango.Core.ExtensionMethods;
+using Newtonsoft.Json.Linq;
+using Tango.BL;
+using Tango.DataStore.Editing;
+using Newtonsoft.Json;
+using Tango.Core;
+
+namespace Tango.PPC.Common.DataStore
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultDataStoreService : ExtendedObject, IDataStoreService, IExternalBridgeRequestHandler
+ {
+ private IDataStoreManager _manager;
+ private IMachineProvider _machineProvider;
+ private List<ListerReceiver> _listenerReceivers;
+
+ private class ListerReceiver
+ {
+ public String Token { get; set; }
+ public ExternalBridgeReceiver Receiver { get; set; }
+ }
+
+ public DefaultDataStoreService(IPPCExternalBridgeService externalBridge, IMachineProvider machineProvider)
+ {
+ externalBridge.RegisterRequestHandler(this);
+
+
+ _listenerReceivers = new List<ListerReceiver>();
+ _machineProvider = machineProvider;
+ machineProvider.MachineOperator.RegisterRequestHandler<PutDataStoreItemRequest>(OnPutDataStoreItemRequest);
+ machineProvider.MachineOperator.RegisterRequestHandler<GetDataStoreItemRequest>(OnGetDataStoreItemRequest);
+ }
+
+ public IDataStoreManager GetManager()
+ {
+ if (_manager == null)
+ {
+ _manager = new EFDataStoreManager();
+ }
+
+ return _manager;
+ }
+
+ public void Dispose()
+ {
+ _manager?.Dispose();
+ }
+
+ #region Generic Handlers
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStorePutRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStorePutRequest(RemoteDataStorePutRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ ValidateCollectionAndKey(request.Collection, request.Key);
+ GetManager().GetCollection(request.Collection).Put(request.Key, request.Value);
+ await receiver.SendGenericResponse(new RemoteDataStorePutResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreGetRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreGetRequest(RemoteDataStoreGetRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ ValidateCollectionAndKey(request.Collection, request.Key);
+
+ if (request.DefaultValue is JObject obj)
+ {
+ request.DefaultValue = DataStoreProtoObject.FromJObject(obj);
+ }
+
+ var item = GetManager().GetCollection(request.Collection).GetItem(request.Key, request.DefaultValue);
+ await receiver.SendGenericResponse(new RemoteDataStoreGetResponse()
+ {
+ DataType = item.Type,
+ Value = item.Value,
+ }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreGetItemRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreGetItemRequest(RemoteDataStoreGetItemRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ ValidateCollectionAndKey(request.Collection, request.Key);
+
+ if (request.DefaultValue is JObject obj)
+ {
+ request.DefaultValue = DataStoreProtoObject.FromJObject(obj);
+ }
+
+ var item = GetManager().GetCollection(request.Collection).GetItem(request.Key, request.DefaultValue);
+ await receiver.SendGenericResponse(new RemoteDataStoreGetItemResponse()
+ {
+ Item = CreateRemoteItem(item)
+ }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreCountRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreCountRequest(RemoteDataStoreCountRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ var count = GetManager().GetCollection(request.Collection).Count();
+ await receiver.SendGenericResponse(new RemoteDataStoreCountResponse()
+ {
+ Count = count
+ }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreDeleteRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreDeleteRequest(RemoteDataStoreDeleteRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ throw new InvalidOperationException("Deleting from the data store is not allowed.");
+ GetManager().GetCollection(request.Collection).Delete(request.Key);
+ await receiver.SendGenericResponse(new RemoteDataStoreDeleteResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreDeleteAllRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreDeleteAllRequest(RemoteDataStoreDeleteAllRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ throw new InvalidOperationException("Deleting from the data store is not allowed.");
+ GetManager().GetCollection(request.Collection).DeleteAll();
+ await receiver.SendGenericResponse(new RemoteDataStoreDeleteAllResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreGetAllRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreGetAllRequest(RemoteDataStoreGetAllRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ var all = GetManager().GetCollection(request.Collection).GetAll();
+ await receiver.SendGenericResponse(new RemoteDataStoreGetAllResponse()
+ {
+ Items = all.Select(x => CreateRemoteItem(x)).ToList()
+ }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreGetCollectionNamesRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreGetCollectionNamesRequest(RemoteDataStoreGetCollectionNamesRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ var names = GetManager().GetCollectionNames();
+ await receiver.SendGenericResponse(new RemoteDataStoreGetCollectionNamesResponse()
+ {
+ Names = names
+ }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreGetAllItemsRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreGetAllItemsRequest(RemoteDataStoreGetAllItemsRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ List<RemoteDataStoreCollection> collections = new List<RemoteDataStoreCollection>();
+
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ var items = db.DataStoreItems.Where(x => !x.IsDeleted).ToList();
+
+ foreach (var itemsGroup in items.GroupBy(x => x.CollectionName))
+ {
+ RemoteDataStoreCollection collection = new RemoteDataStoreCollection();
+ collection.Name = itemsGroup.First().CollectionName;
+ collections.Add(collection);
+
+ foreach (var item in itemsGroup)
+ {
+ collection.Items.Add(CreateRemoteItem(item.ToDataStoreItem()));
+ }
+ }
+ }
+
+ await receiver.SendGenericResponse(new RemoteDataStoreGetAllItemsResponse()
+ {
+ Collections = collections
+ }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(UpdateDataStoreRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnUpdateDataStoreRequest(UpdateDataStoreRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ var allItems = db.DataStoreItems.ToList();
+
+ List<BL.Entities.DataStoreItem> deleted = new List<BL.Entities.DataStoreItem>();
+
+ foreach (var guid in request.ToDelete)
+ {
+ var item = allItems.FirstOrDefault(x => x.Guid == guid);
+ if (item != null)
+ {
+ item.IsDeleted = true;
+ item.IsSynchronized = true;
+ item.LastUpdated = DateTime.UtcNow;
+ deleted.Add(item);
+ }
+ }
+
+ foreach (var item in request.ToUpsert)
+ {
+ ValidateCollectionAndKey(item.CollectionName, item.Key);
+ }
+
+ foreach (var item in request.ToUpsert)
+ {
+ var itemDb = allItems.FirstOrDefault(x => x.CollectionName == item.CollectionName && x.Key == item.Key);
+
+ if (itemDb == null)
+ {
+ itemDb = new BL.Entities.DataStoreItem();
+ itemDb.Guid = item.Guid;
+ db.DataStoreItems.Add(itemDb);
+ }
+
+ itemDb.CollectionName = item.CollectionName;
+ itemDb.DataType = item.DataType;
+ itemDb.IsDeleted = item.IsDeleted;
+ itemDb.IsSynchronized = true;
+ itemDb.Key = item.Key;
+ itemDb.LastUpdated = item.LastUpdated;
+ itemDb.Value = item.Value;
+ }
+
+ db.SaveChanges();
+
+ if (_machineProvider.IsConnected)
+ {
+ Core.Threading.ThreadFactory.StartNew(() =>
+ {
+ foreach (var item in request.ToUpsert)
+ {
+ try
+ {
+ var response = _machineProvider.MachineOperator.SendRequest<DataStoreItemModifiedRequest, DataStoreItemModifiedResponse>(new DataStoreItemModifiedRequest()
+ {
+ Collection = item.CollectionName,
+ Key = item.Key
+ }).Result;
+ }
+ catch (Exception ex)
+ {
+ Logging.LogManager.Default.Log(ex, $"Error notifying firmware about data store item change '{item.CollectionName}.{item.Key}'.");
+ }
+ }
+
+ foreach (var item in deleted)
+ {
+ try
+ {
+ var response = _machineProvider.MachineOperator.SendRequest<DataStoreItemModifiedRequest, DataStoreItemModifiedResponse>(new DataStoreItemModifiedRequest()
+ {
+ Collection = item.CollectionName,
+ Key = item.Key
+ }).Result;
+ }
+ catch (Exception ex)
+ {
+ Logging.LogManager.Default.Log(ex, $"Error notifying firmware about data store item change '{item.CollectionName}.{item.Key}'.");
+ }
+ }
+ });
+ }
+ }
+
+ await receiver.SendGenericResponse(new UpdateDataStoreResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDataStoreStartListenRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDataStoreStartListenRequest(RemoteDataStoreStartListenRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ _listenerReceivers.Add(new ListerReceiver() { Receiver = receiver, Token = token });
+
+ await receiver.SendGenericResponse(new RemoteDataStoreStartListenResponse()
+ {
+ ChangeType = RemoteDataStoreChangeType.None,
+ Item = null
+ }, token);
+ }
+
+ private RemoteDataStoreItem CreateRemoteItem(IDataStoreItem item)
+ {
+ RemoteDataStoreItem remote = new RemoteDataStoreItem();
+
+ item.MapPropertiesTo(remote, MappingFlags.All);
+
+ return remote;
+ }
+
+ #endregion
+
+ #region Proto Handlers
+
+ private async void OnPutDataStoreItemRequest(ITransporter transporter, PutDataStoreItemRequest request, string token)
+ {
+ try
+ {
+ ValidateCollectionAndKey(request.Collection, request.Key);
+
+ GetManager().GetCollection(request.Collection).Put(request.Key, GetPMRValue(request.Item));
+ await transporter.SendResponse(new PutDataStoreItemResponse(), token);
+
+ try
+ {
+ if (_listenerReceivers.Count > 0)
+ {
+ var item = GetManager().GetCollection(request.Collection).GetItem(request.Key);
+ var remoteItem = CreateRemoteItem(item);
+
+ foreach (var listener in _listenerReceivers.ToList())
+ {
+ try
+ {
+ await listener.Receiver.SendGenericResponse(new RemoteDataStoreStartListenResponse()
+ {
+ ChangeType = RemoteDataStoreChangeType.Modified,
+ CollectionName = request.Collection,
+ Item = remoteItem
+ }, listener.Token);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, $"Error sending data store item notification to receiver '{listener.Receiver.Adapter.ToString()}'");
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error generating data store item notifications.");
+ }
+ }
+ catch (Exception ex)
+ {
+ try
+ {
+ await transporter.SendResponse(new PutDataStoreItemResponse(), token, new TransportResponseConfig()
+ {
+ ErrorCode = PMR.Common.ErrorCode.GeneralDatastoreError,
+ ErrorMessage = ex.Message
+ });
+ }
+ catch (Exception exx)
+ {
+ Debug.WriteLine(exx);
+ }
+ }
+ }
+
+ private async void OnGetDataStoreItemRequest(ITransporter transporter, GetDataStoreItemRequest request, string token)
+ {
+ try
+ {
+ ValidateCollectionAndKey(request.Collection, request.Key);
+
+ var item = GetManager().GetCollection(request.Collection).GetItem(request.Key, GetPMRValue(request.DefaultItem));
+ await transporter.SendResponse(new GetDataStoreItemResponse()
+ {
+ Key = item.Key,
+ Item = CreatePMRDataStoreItem(item),
+ }, token);
+ }
+ catch (KeyNotFoundException ex)
+ {
+ try
+ {
+ await transporter.SendResponse(new GetDataStoreItemResponse(), token, new TransportResponseConfig()
+ {
+ ErrorCode = PMR.Common.ErrorCode.KeyNotFound,
+ ErrorMessage = ex.Message
+ });
+ }
+ catch (Exception exx)
+ {
+ Debug.WriteLine(exx);
+ }
+ }
+ catch (Exception ex)
+ {
+ try
+ {
+ await transporter.SendResponse(new GetDataStoreItemResponse(), token, new TransportResponseConfig()
+ {
+ ErrorCode = PMR.Common.ErrorCode.GeneralDatastoreError,
+ ErrorMessage = ex.Message
+ });
+ }
+ catch (Exception exx)
+ {
+ Debug.WriteLine(exx);
+ }
+ }
+ }
+
+ #region Helpers
+
+ private DataStoreItem CreatePMRDataStoreItem(IDataStoreItem item)
+ {
+ DataStoreItem pmr = new DataStoreItem();
+ pmr.DataType = (PMR.DataStore.DataType)item.Type;
+
+ switch (item.Type)
+ {
+ case Tango.DataStore.DataType.Int32:
+ pmr.Int32Value = (int)Convert.ChangeType(item.Value, typeof(int));
+ break;
+ case Tango.DataStore.DataType.Float:
+ pmr.FloatValue = (float)Convert.ChangeType(item.Value, typeof(float));
+ break;
+ case Tango.DataStore.DataType.Double:
+ pmr.DoubleValue = (double)Convert.ChangeType(item.Value, typeof(double));
+ break;
+ case Tango.DataStore.DataType.Boolean:
+ pmr.BooleanValue = (bool)Convert.ChangeType(item.Value, typeof(bool));
+ break;
+ case Tango.DataStore.DataType.String:
+ pmr.StringValue = (String)Convert.ChangeType(item.Value, typeof(String));
+ break;
+ case Tango.DataStore.DataType.Bytes:
+ pmr.BytesValue = Google.Protobuf.ByteString.CopyFrom((byte[])Convert.ChangeType(item.Value, typeof(byte[])));
+ break;
+ case Tango.DataStore.DataType.Proto:
+ DataStoreProtoObject proto = item.Value as DataStoreProtoObject;
+ pmr.BytesValue = Google.Protobuf.ByteString.CopyFrom(proto.Data);
+ pmr.ProtoType = proto.MessageType;
+ break;
+ }
+
+ return pmr;
+ }
+
+ private Object GetPMRValue(DataStoreItem item)
+ {
+ if (item == null) return null;
+
+ switch (item.DataType)
+ {
+ case PMR.DataStore.DataType.Int32:
+ return item.Int32Value;
+ case PMR.DataStore.DataType.Float:
+ return item.FloatValue;
+ case PMR.DataStore.DataType.Double:
+ return item.DoubleValue;
+ case PMR.DataStore.DataType.Boolean:
+ return item.BooleanValue;
+ case PMR.DataStore.DataType.String:
+ return item.StringValue;
+ case PMR.DataStore.DataType.Bytes:
+ return item.BytesValue.ToByteArray();
+ case PMR.DataStore.DataType.Proto:
+ return DataStoreProtoObject.FromPMRDataStoreItem(item);
+ }
+
+ throw new NotSupportedException("The specified data type if not supported.");
+ }
+
+ #endregion
+
+ #endregion
+
+ private void ValidateCollectionAndKey(String collection = null, String key = null)
+ {
+ if (collection != null)
+ {
+ if (!DataStoreHelper.ValidateCollectionOrKeyName(collection))
+ {
+ throw new ArgumentException("Collection name contains invalid characters.");
+ }
+ }
+
+ if (key != null)
+ {
+ if (!DataStoreHelper.ValidateCollectionOrKeyName(key))
+ {
+ throw new ArgumentException("Item key contains invalid characters.");
+ }
+ }
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ //Do nothing.
+ _listenerReceivers.RemoveAll(x => x.Receiver == receiver);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/DataStore/IDataStoreService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/DataStore/IDataStoreService.cs
new file mode 100644
index 000000000..94ae3fa30
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/DataStore/IDataStoreService.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.DataStore;
+
+namespace Tango.PPC.Common.DataStore
+{
+ public interface IDataStoreService : IDisposable
+ {
+ IDataStoreManager GetManager();
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs
index f9674e409..ee96a77a5 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/DefaultEventLogger.cs
@@ -16,6 +16,10 @@ using Tango.Integration.Operation;
using Tango.PPC.Common.Application;
using Tango.PPC.Common.Authentication;
using Tango.PPC.Common.Connection;
+using Tango.Transport;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.PPC.Shared.Events;
+using Tango.Core.DI;
namespace Tango.PPC.Common.EventLogging
{
@@ -35,6 +39,7 @@ namespace Tango.PPC.Common.EventLogging
private bool _isInitialized;
private List<MachinesEvent> _pendingEvents;
private List<MachinesEvent> _currentEvents;
+ private Machine _machine;
#region Events
@@ -50,6 +55,21 @@ namespace Tango.PPC.Common.EventLogging
#endregion
+ private IPPCExternalBridgeService _externalBridge;
+ [TangoInject(Mode = TangoInjectMode.WhenAvailable)]
+ public IPPCExternalBridgeService ExternalBridgeService
+ {
+ get { return _externalBridge; }
+ set
+ {
+ if (_externalBridge != value)
+ {
+ _externalBridge = value;
+ _externalBridge.RegisterRequestHandler(this);
+ }
+ }
+ }
+
#region Constructors
/// <summary>
@@ -84,7 +104,6 @@ namespace Tango.PPC.Common.EventLogging
_machineProvider.MachineOperator.PrintingAborted += MachineOperator_PrintingAborted;
_machineProvider.MachineOperator.PrintingCompleted += MachineOperator_PrintingCompleted;
_machineProvider.MachineOperator.PrintingFailed += MachineOperator_PrintingFailed;
-
}
#endregion
@@ -99,18 +118,28 @@ namespace Tango.PPC.Common.EventLogging
{
_db = ObservablesContext.CreateDefault();
+ _machine = _db.Machines.FirstOrDefault();
+
_db.EventTypes.ToList();
foreach (var type in _db.EventTypes)
{
- _eventTypesGuids.Add((EventTypes)type.Code, type);
+ try
+ {
+ _eventTypesGuids.Add((EventTypes)type.Code, type);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, $"Error initializing event type '{type.Name}'.");
+ }
}
_isInitialized = true;
}
- catch
+ catch (Exception ex)
{
_isInitialized = false;
+ LogManager.Log(ex, "Error initializing event types.");
}
}
}
@@ -250,7 +279,7 @@ namespace Tango.PPC.Common.EventLogging
machineEvent.HostName = _hostName;
machineEvent.EventType = _eventTypesGuids[machineEvent.Type];
- if (_machineProvider.MachineOperator == null || _authentication.CurrentUser == null)
+ if (_machine == null)
{
_pendingEvents.Add(machineEvent);
}
@@ -268,14 +297,14 @@ namespace Tango.PPC.Common.EventLogging
}
LogManager.Log("Logging event " + machineEvent.EventType.Name);
- machineEvent.MachineGuid = _machineProvider.Machine.Guid;
- machineEvent.UserGuid = _authentication.CurrentUser.Guid;
- machineEvent.User = _authentication.CurrentUser;
+ machineEvent.MachineGuid = _machine.Guid;
+ machineEvent.UserGuid = null;
+ machineEvent.User = null;
_events.Enqueue(machineEvent);
if (!_currentEvents.Exists(x => x.Type == machineEvent.Type))
{
- if (machineEvent.Group != EventTypeGroups.Application && machineEvent.Group != EventTypeGroups.Transport)
+ if (machineEvent.Group != EventTypeGroups.Application && machineEvent.Group != EventTypeGroups.Transport && machineEvent.Group != EventTypeGroups.Jobs)
{
_currentEvents.Add(machineEvent);
}
@@ -385,5 +414,21 @@ namespace Tango.PPC.Common.EventLogging
}
#endregion
+
+ #region External Bridge Handler
+
+ [ExternalBridgeRequestHandlerMethod(typeof(PushEmulatedEventRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnStartPerformanceUpdatesRequest(PushEmulatedEventRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ _machineProvider.MachineOperator.PushEmulatedEvent(request.Event, request.Timeout);
+ await receiver.SendGenericResponse(new PushEmulatedEventResponse(), token);
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+
+ }
+
+ #endregion
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/IEventLogger.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/IEventLogger.cs
index 10560e034..81cce927d 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/IEventLogger.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/EventLogging/IEventLogger.cs
@@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
+using Tango.Integration.ExternalBridge;
using Tango.PMR.Diagnostics;
namespace Tango.PPC.Common.EventLogging
@@ -12,7 +13,7 @@ namespace Tango.PPC.Common.EventLogging
/// <summary>
/// Represents a database events logger.
/// </summary>
- public interface IEventLogger
+ public interface IEventLogger : IExternalBridgeRequestHandler
{
/// <summary>
/// Occurs when a new machine event has been received.
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/ExternalBridge/PPCExternalBridgeService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/ExternalBridge/PPCExternalBridgeService.cs
index c50202e86..99951d812 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/ExternalBridge/PPCExternalBridgeService.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/ExternalBridge/PPCExternalBridgeService.cs
@@ -1,12 +1,16 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.BL.Entities;
using Tango.Core.DI;
+using Tango.Core.Helpers;
+using Tango.CSV;
using Tango.Integration.ExternalBridge;
using Tango.Integration.Operation;
+using Tango.PMR.Common;
using Tango.PPC.Common.Application;
using Tango.PPC.Common.Connection;
using Tango.PPC.Common.Messages;
@@ -14,6 +18,11 @@ using Tango.Settings;
namespace Tango.PPC.Common.ExternalBridge
{
+ public class CsvEntry
+ {
+ public String MessageType { get; set; }
+ }
+
/// <summary>
/// Represents the PPC external bridge service capable of exposing a remote API for communicating and controlling the machine through the PPC.
/// </summary>
@@ -28,11 +37,36 @@ namespace Tango.PPC.Common.ExternalBridge
/// <param name="machineProvider">The machine provider.</param>
public PPCExternalBridgeService(IPPCApplicationManager applicationManager, IMachineProvider machineProvider)
{
- applicationManager.ApplicationReady += (_, __) =>
+ var csvStream = EmbeddedResourceHelper.GetEmbeddedResourceStream("Tango.PPC.Common.SafetyLevelOperations.csv");
+
+ List<CsvEntry> entries = CsvFile.Read<CsvEntry>(new CsvSource(csvStream)).ToList();
+
+ foreach (var entry in entries)
+ {
+ MessageType type;
+ if (Enum.TryParse<MessageType>(entry.MessageType, out type))
+ {
+ ExternalBridgeService.SafetyLevelOperations.Add(type);
+ }
+ }
+
+ applicationManager.ApplicationReady += (_, __) =>
{
var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+ //TODO: Configure external bridge for configure protocol enforce...
MachineOperator = machineProvider.MachineOperator;
Machine = machineProvider.Machine;
+ SignalRConfiguration.Enabled = settings.EnableExternalBridgeSignalR;
+ TcpTransportAdapterWriteMode = settings.TcpTransportAdapterWriteMode;
+ if (Environment.CommandLine.Contains("-webDebug"))
+ {
+ SignalRConfiguration.Address = "http://localhost:1111/"; //settings.DeploymentSlot.ToAddress();
+ }
+ else
+ {
+ SignalRConfiguration.Address = settings.DeploymentSlot.ToAddress();
+ }
+ SignalRConfiguration.Hub = settings.ExternalBridgeSignalRHub;
Enabled = settings.EnableExternalBridge;
};
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs
new file mode 100644
index 000000000..8272ea34d
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/DefaultFileSystemService.cs
@@ -0,0 +1,433 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Core.IO;
+using Tango.FileSystem;
+using Tango.FileSystem.Network;
+using Tango.Integration.ExternalBridge;
+using Tango.Integration.Operation;
+using Tango.Logging;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.PPC.Shared.Logs;
+using Tango.Settings;
+using Tango.Transport;
+using Tango.Transport.Transporters;
+using Tango.WebRTC;
+
+namespace Tango.PPC.Common.FileSystem
+{
+ /// <summary>
+ /// Represents the <see cref="IFileSystemService"/> default implementation.
+ /// </summary>
+ /// <seealso cref="Tango.Core.ExtendedObject" />
+ /// <seealso cref="Tango.PPC.Common.FileSystem.IFileSystemService" />
+ /// <seealso cref="Tango.Integration.ExternalBridge.IExternalBridgeRequestHandler" />
+ [TangoCreateWhenRegistered]
+ public class DefaultFileSystemService : ExtendedObject, IFileSystemService, IExternalBridgeRequestHandler
+ {
+ private FileSystemManager _manager;
+ private Dictionary<String, FileSystemOperation> _operations;
+ private Dictionary<ExternalBridgeReceiver, BasicTransporter> _webRtcClients;
+ private PPCSettings _settings;
+
+ public bool Enabled { get; set; } = true;
+ public bool EnableWebRTC { get; set; } = true;
+
+ public DefaultFileSystemService(IPPCExternalBridgeService externalBridge)
+ {
+ _webRtcClients = new Dictionary<ExternalBridgeReceiver, BasicTransporter>();
+ _manager = new FileSystemManager();
+ _operations = new Dictionary<string, FileSystemOperation>();
+ externalBridge.RegisterRequestHandler(this);
+ _settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(InitWebRtcRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnInitWebRtcRequest(InitWebRtcRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ try
+ {
+ if (!EnableWebRTC)
+ {
+ await receiver.SendErrorResponse(new InvalidOperationException("The file system service WebRTC channel is disabled on this machine."), token);
+ return;
+ }
+
+ LogManager.Log("Initializing WebRTC channel for file system service.");
+
+ if (_webRtcClients.ContainsKey(receiver))
+ {
+ _webRtcClients[receiver].Dispose();
+ }
+
+ LogManager.Log("Initializing WebRTC transport adapter on 'Passive' mode.");
+ var webRtcAdapter = new WebRtcTransportAdapter(receiver, WebRtcTransportAdapterMode.Passive, request.DataChannelName)
+ {
+ EnableCompression = receiver.Adapter.EnableCompression
+ };
+ webRtcAdapter.Ready += (x, e) =>
+ {
+ LogManager.Log("The file system service WebRTC channel is ready.");
+ };
+
+ BasicTransporter webRtcTransporter = new BasicTransporter(webRtcAdapter);
+ webRtcTransporter.GenericProtocol = receiver.GenericProtocol;
+ webRtcTransporter.ComponentName = "File System Passive WebRTC Transporter";
+ webRtcTransporter.UseKeepAlive = false;
+ webRtcTransporter.RegisterRequestHandler<ChunkDownloadRequest>(WebRtcChunkDownloadRequestReceived);
+ webRtcTransporter.RegisterRequestHandler<ChunkUploadRequest>(WebRtcChunkUploadRequestReceived);
+ await webRtcTransporter.Connect();
+
+ LogManager.Log("Sending WebRTC initialization response...");
+
+ await receiver.SendGenericResponse(new InitWebRtcResponse(), token);
+ _webRtcClients[receiver] = webRtcTransporter;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error initializing WebRTC channel for file system service.");
+ await receiver.SendErrorResponse(ex, token);
+ }
+ }
+
+ private async void WebRtcChunkDownloadRequestReceived(ITransporter transporter, ChunkDownloadRequest request, string token)
+ {
+ await OnChunkDownloadRequest(request, token, transporter);
+ }
+
+ private async void WebRtcChunkUploadRequestReceived(ITransporter transporter, ChunkUploadRequest request, string token)
+ {
+ await OnChunkUploadRequest(request, token, transporter);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(GetFileSystemItemRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnGetFileSystemItemRequest(GetFileSystemItemRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ FileSystemItemDTO dto = _manager.GetFolder(request);
+ await receiver.SendGenericResponse(new GetFileSystemItemResponse() { FileSystemItem = dto }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(FileUploadRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnFileUploadRequest(FileUploadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ var tempFile = TemporaryManager.CreateFile();
+ using (var stream = new FileStream(tempFile, FileMode.Create)) { }
+
+ FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, tempFile) { UploadPostPath = request.Path };
+ _operations.Add(operation.Id, operation);
+
+ await receiver.SendGenericResponse(new FileUploadResponse() { OperationId = operation.Id }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(FolderUploadRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnFolderUploadRequest(FolderUploadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ var tempFile = TemporaryManager.CreateFile();
+ using (var stream = new FileStream(tempFile, FileMode.Create)) { }
+
+ FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, tempFile) { UploadPostPath = request.Path, IsPathTempZip = true };
+ _operations.Add(operation.Id, operation);
+
+ await receiver.SendGenericResponse(new FolderUploadResponse() { OperationId = operation.Id }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(FileDownloadRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnFileDownloadRequest(FileDownloadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ if (!File.Exists(request.Path))
+ {
+ throw new FileNotFoundException("Could not find the specified file.");
+ }
+
+ FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Download, request.Path);
+
+ _operations.Add(operation.Id, operation);
+
+ await receiver.SendGenericResponse(new FileDownloadResponse()
+ {
+ OperationId = operation.Id,
+ Length = new FileInfo(request.Path).Length
+ }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(FolderDownloadRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnFolderDownloadRequest(FolderDownloadRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ if (!Directory.Exists(request.Path))
+ {
+ throw new FileNotFoundException("Could not find the specified directory.");
+ }
+
+ var tempFile = TemporaryManager.CreateImaginaryFile();
+
+ ZipFile.CreateFromDirectory(request.Path, tempFile);
+
+ FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Download, tempFile);
+ operation.IsPathTempZip = true;
+
+ _operations.Add(operation.Id, operation);
+
+ await receiver.SendGenericResponse(new FolderDownloadResponse()
+ {
+ OperationId = operation.Id,
+ Length = new FileInfo(tempFile).Length
+ }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(ChunkUploadRequest))]
+ public async Task OnChunkUploadRequest(ChunkUploadRequest request, String token, ITransporter receiver)
+ {
+ this.ThrowIfDisabled();
+
+ FileSystemOperation operation;
+ _operations.TryGetValue(request.OperationId, out operation);
+
+ if (operation == null)
+ {
+ throw new ArgumentException("Invalid operation id.");
+ }
+
+ using (var stream = new FileStream(operation.Path, FileMode.Append))
+ {
+ stream.Write(request.Data, 0, request.Data.Length);
+ }
+
+ if (request.IsCompleted)
+ {
+ if (!operation.IsPathTempZip)
+ {
+ File.Copy(operation.Path, operation.UploadPostPath, true);
+ try
+ {
+ File.Delete(operation.Path);
+ }
+ catch { }
+ }
+ else
+ {
+ using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile(operation.Path))
+ {
+ zip.ExtractAll(operation.UploadPostPath, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
+ }
+
+ try
+ {
+ File.Delete(operation.Path);
+ }
+ catch { }
+ }
+ }
+
+ await receiver.SendGenericResponse(new ChunkUploadResponse(), token, new TransportResponseConfig() { Priority = QueuePriority.Low });
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(ChunkDownloadRequest))]
+ public async Task OnChunkDownloadRequest(ChunkDownloadRequest request, String token, ITransporter receiver)
+ {
+ this.ThrowIfDisabled();
+
+ FileSystemOperation operation;
+ _operations.TryGetValue(request.OperationId, out operation);
+
+ if (operation == null)
+ {
+ throw new ArgumentException("Invalid operation id.");
+ }
+
+ FileStream stream = null;
+ bool removeTempZipFile = false;
+
+ try
+ {
+ stream = new FileStream(operation.Path, FileMode.Open);
+ stream.Position = request.Position;
+ byte[] data = new byte[Math.Min(request.MaxChunkSize, stream.Length - stream.Position)];
+
+ if (stream.Position + data.Length == stream.Length)
+ {
+ removeTempZipFile = true;
+ }
+
+ await stream.ReadAsync(data, 0, data.Length);
+ stream.Dispose();
+ stream = null;
+ await receiver.SendGenericResponse(new ChunkDownloadResponse()
+ {
+ Data = data
+ }, token, new TransportResponseConfig() { Priority = QueuePriority.Low });
+ }
+ catch (Exception ex)
+ {
+ stream?.Dispose();
+ throw ex;
+ }
+ finally
+ {
+ if (operation.IsPathTempZip && removeTempZipFile)
+ {
+ try
+ {
+ if (File.Exists(operation.Path))
+ {
+ File.Delete(operation.Path);
+ }
+ }
+ catch { }
+ }
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(AbortOperationRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnAbortOperationRequest(AbortOperationRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ FileSystemOperation operation;
+ _operations.TryGetValue(request.OperationId, out operation);
+
+ if (operation == null)
+ {
+ throw new ArgumentException("Invalid operation id.");
+ }
+
+ if (operation.Mode == FileSystemOperationMode.Upload)
+ {
+ if (File.Exists(operation.Path))
+ {
+ File.Delete(operation.Path);
+ }
+ }
+ else if (operation.IsPathTempZip)
+ {
+ if (File.Exists(operation.Path))
+ {
+ File.Delete(operation.Path);
+ }
+ }
+
+ await receiver.SendGenericResponse(new AbortOperationResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(MoveRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnMoveRequest(MoveRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ _manager.Move(request);
+ await receiver.SendGenericResponse(new MoveResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(CopyRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnCopyRequest(CopyRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ _manager.Copy(request);
+ await receiver.SendGenericResponse(new CopyResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(DeleteRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnDeleteRequest(DeleteRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ _manager.Delete(request.Path);
+ await receiver.SendGenericResponse(new DeleteResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(CreateFolderRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnCreateFolderRequest(CreateFolderRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ var dto = _manager.CreateFolder(request.Path, request.FolderName);
+ await receiver.SendGenericResponse(new CreateFolderResponse() { FolderItem = dto }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(PerformDiskSpaceOptimizationRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnPerformDiskSpaceOptimizationRequest(PerformDiskSpaceOptimizationRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ var deletedBytes = _manager.PerformDiskSpaceOptimization();
+ await receiver.SendGenericResponse(new PerformDiskSpaceOptimizationResponse() { DeletedBytes = deletedBytes }, token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(GetLogFilesRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnGetLogFilesRequest(GetLogFilesRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ FolderItem folder = null;
+
+ if (request.LogFileType == RemoteLogFileType.Application)
+ {
+ var fileLogger = LogManager.RegisteredLoggers.SingleOrDefault(x => x.GetType() == typeof(FileLogger)) as FileLogger;
+
+ if (fileLogger == null)
+ {
+ throw new InvalidOperationException("Could not locate the application file logger.");
+ }
+
+ folder = await _manager.GetFolder(fileLogger.Folder, false, "*.log") as FolderItem;
+ }
+ else
+ {
+ if (MachineOperator.EmbeddedLogsFolder == null)
+ {
+ throw new InvalidOperationException("The firmware file logger folder could not be read.");
+ }
+
+ folder = await _manager.GetFolder(MachineOperator.EmbeddedLogsFolder, false, "*.log") as FolderItem;
+ }
+
+ GetLogFilesResponse response = new GetLogFilesResponse();
+
+ foreach (var file in folder.Items.OfType<FileItem>().OrderByDescending(x => x.DateCreated).DistinctBy(x => x.Name))
+ {
+ response.LogFiles.Add(new RemoteLogFile()
+ {
+ DateModified = file.DateModified,
+ DateCreated = file.DateCreated,
+ Name = file.Name,
+ Path = file.Path,
+ Length = new FileInfo(file.Path).Length
+ });
+ }
+
+ await receiver.SendGenericResponse(response, token);
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ if (_webRtcClients.ContainsKey(receiver))
+ {
+ try
+ {
+ LogManager.Log("External bridge receiver disconnected. Disposing file system service WebRTC channel...");
+ var webRtcTransporter = _webRtcClients[receiver];
+ _webRtcClients.Remove(receiver);
+ webRtcTransporter.Dispose();
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error disposing the WebRTC channel.");
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/FileSystemOperation.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/FileSystemOperation.cs
new file mode 100644
index 000000000..9fba7a874
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/FileSystemOperation.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.FileSystem
+{
+ /// <summary>
+ /// Represents an active file system file/folder download/upload operation
+ /// </summary>
+ public class FileSystemOperation
+ {
+ /// <summary>
+ /// Gets or sets the operation mode.
+ /// </summary>
+ public FileSystemOperationMode Mode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the operation identifier.
+ /// </summary>
+ public String Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the path for the operation.
+ /// </summary>
+ public String Path { get; set; }
+
+ /// <summary>
+ /// Should be set to true when the <see cref="Path"/> is a temporary zip file when performing download of a folder.
+ /// </summary>
+ public bool IsPathTempZip { get; set; }
+
+ /// <summary>
+ /// Gets or sets the actual path to extract the zip file when uploading folder.
+ /// </summary>
+ public String UploadPostPath { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="FileSystemOperation"/> class.
+ /// </summary>
+ /// <param name="mode">The mode.</param>
+ /// <param name="path">The path.</param>
+ public FileSystemOperation(FileSystemOperationMode mode, String path)
+ {
+ Mode = mode;
+ Id = Guid.NewGuid().ToString();
+ Path = path;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/UpdatePackageFile.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/FileSystemOperationMode.cs
index df496c3be..e28843bce 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/UpdatePackageFile.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/FileSystemOperationMode.cs
@@ -4,10 +4,11 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Tango.PPC.Common.MachineUpdate
+namespace Tango.PPC.Common.FileSystem
{
- public class UpdatePackageFile
+ public enum FileSystemOperationMode
{
- public Version Version { get; set; }
+ Upload,
+ Download
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs
new file mode 100644
index 000000000..7a80db9c7
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/FileSystem/IFileSystemService.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.FileSystem
+{
+ /// <summary>
+ /// Represents a PPC file system remote service.
+ /// </summary>
+ /// <seealso cref="Tango.PPC.Common.IPPCService" />
+ public interface IFileSystemService : IPPCService
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable the WebRTC transport channel.
+ /// </summary>
+ bool EnableWebRTC { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Helpers/KeyboardHelper.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Helpers/KeyboardHelper.cs
new file mode 100644
index 000000000..202f0378e
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Helpers/KeyboardHelper.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Touch.Keyboard;
+
+namespace Tango.PPC.Common.Helpers
+{
+ public static class KeyboardHelper
+ {
+ public static void OpenKeyboard(KeyboardActionKeyMode action, TouchKeyboardMode mode = TouchKeyboardMode.AlphaNumeric)
+ {
+ KeyboardView.Keyboard.ActionKeyMode = action;
+ KeyboardView.Keyboard.Mode = mode;
+ KeyboardView.Default.IsOpened = true;
+ }
+
+ public static void CloseKeyboard()
+ {
+ KeyboardView.Default.IsOpened = false;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Helpers/LogsHelper.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Helpers/LogsHelper.cs
new file mode 100644
index 000000000..b7ab2d5b8
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Helpers/LogsHelper.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Logging;
+
+namespace Tango.PPC.Common.Helpers
+{
+ public static class LogsHelper
+ {
+ private static LogSafe _logSafe;
+
+ public static void SetLogSafe(LogSafe logSafe)
+ {
+ _logSafe = logSafe;
+ }
+
+ public static LogSafe GetLogSafe()
+ {
+ return _logSafe;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/IPPCService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/IPPCService.cs
new file mode 100644
index 000000000..5dfe5335c
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/IPPCService.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.PPC.Common;
+
+namespace Tango.PPC.Common
+{
+ /// <summary>
+ /// Represents a PPC remoting service.
+ /// </summary>
+ public interface IPPCService
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether this <see cref="IPPCService"/> is enabled.
+ /// </summary>
+ bool Enabled { get; set; }
+ }
+}
+
+public static class ExtensionMethods
+{
+ /// <summary>
+ /// Throws an exception is the service is disabled.
+ /// </summary>
+ /// <param name="service">The service.</param>
+ /// <exception cref="System.InvalidOperationException"></exception>
+ public static void ThrowIfDisabled(this IPPCService service)
+ {
+ if (!service.Enabled)
+ {
+ throw new NotSupportedException($"The {service.GetType().Name} is currently disabled. Could not perform the requested action.");
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Images/cl-full.png b/Software/Visual_Studio/PPC/Tango.PPC.Common/Images/cl-full.png
new file mode 100644
index 000000000..5aaea8e6c
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Images/cl-full.png
Binary files differ
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Images/lubricant2.png b/Software/Visual_Studio/PPC/Tango.PPC.Common/Images/lubricant2.png
new file mode 100644
index 000000000..554c16305
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Images/lubricant2.png
Binary files differ
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs
new file mode 100644
index 000000000..75c5ae9cd
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/DefaultInsightsService.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Insights;
+using Tango.Integration.ExternalBridge;
+using Tango.Logging;
+using Tango.PPC.Common.Application;
+using Tango.PPC.Common.Connection;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.PPC.Shared.Insights;
+using Tango.Settings;
+
+namespace Tango.PPC.Common.Insights
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultInsightsService : ExtendedObject, IInsightsService, IExternalBridgeRequestHandler
+ {
+ private InsightsListener _listener;
+
+ private IMachineProvider MachineProvider { get; set; }
+
+ public DefaultInsightsService(IPPCExternalBridgeService externalBridge, IMachineProvider machineProvider, IPPCApplicationManager applicationManager)
+ {
+ externalBridge.RegisterRequestHandler(this);
+ MachineProvider = machineProvider;
+ applicationManager.ApplicationStarted += ApplicationManager_ApplicationStarted;
+ }
+
+ private void ApplicationManager_ApplicationStarted(object sender, EventArgs e)
+ {
+ Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+ settings.Save();
+
+ if (settings.InsightsEnabled)
+ {
+ LogManager.Log("Starting insights service...");
+
+ _listener = new InsightsListener(MachineProvider.MachineOperator);
+ _listener.SamplingInterval = settings.InsightsSamplingInterval;
+ _listener.StorageCleanupInterval = settings.InsightsStorageCleanupInterval;
+ _listener.MaxStorageDuration = settings.InsightsMaxStorageDuration;
+
+ LogManager.Log($"Insights configuration:\nSampling Interval: {_listener.SamplingInterval}\nStorage Cleanup Interval: {_listener.StorageCleanupInterval}\nMax Storage Duration: {_listener.MaxStorageDuration}");
+
+ _listener.Start();
+ }
+ else
+ {
+ LogManager.Log("Insights service is disabled.", LogCategory.Warning);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error initializing insights listener.");
+ }
+ });
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(InsightsRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnInsightsRequest(InsightsRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ try
+ {
+ InsightsFile insightsFile = new InsightsFile();
+ var filePath = TemporaryManager.CreateImaginaryFile();
+
+ await Task.Factory.StartNew(() =>
+ {
+ var frames = InsightsManager.Default.GetFrames(request.StartDateUTC, request.EndDateUTC);
+ insightsFile.Frames = frames;
+
+ if (request.IncludeEvents)
+ {
+ insightsFile.Events = InsightsManager.Default.GetEvents(request.StartDateUTC, request.EndDateUTC);
+ }
+
+ if (request.IncludeStatuses)
+ {
+ insightsFile.Statuses = InsightsManager.Default.GetStatuses(request.StartDateUTC, request.EndDateUTC);
+ }
+
+ if (request.IncludeApplicationExceptions)
+ {
+ insightsFile.ApplicationExceptions = InsightsManager.Default.GetApplicationExceptions(request.StartDateUTC, request.EndDateUTC);
+ }
+
+ insightsFile.ToFile(filePath);
+ });
+
+ await receiver.SendGenericResponse(new InsightsResponse()
+ {
+ InisightsFilePath = filePath,
+ InsightsFileLength = new FileInfo(filePath).Length
+ }, token);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error processing insights request.");
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(InsightsDownloadCompletedRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnInsightsDownloadCompletedRequest(InsightsDownloadCompletedRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ await Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ File.Delete(request.InisightsFilePath);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error deleting insights request temp file after download completion.");
+ }
+ });
+ await receiver.SendGenericResponse(new InsightsResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(InsightsMinDateRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnInsightsMinDateRequest(InsightsMinDateRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ DateTime? minDate = null;
+
+ try
+ {
+ await Task.Factory.StartNew(() =>
+ {
+ minDate = InsightsManager.Default.GetFramesMinDate();
+ });
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error retrieving insights frames minimum date.");
+ }
+
+ await receiver.SendGenericResponse(new InsightsMinDateResponse() { MinDate = minDate }, token);
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ //Do Nothing...
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/IInsightsService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/IInsightsService.cs
new file mode 100644
index 000000000..268bb269b
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Insights/IInsightsService.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.Insights
+{
+ public interface IInsightsService
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineSetup/MachineSetupManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineSetup/MachineSetupManager.cs
index 004c37096..15902f629 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineSetup/MachineSetupManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineSetup/MachineSetupManager.cs
@@ -11,12 +11,15 @@ using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Tango.BL;
+using Tango.BL.Entities;
using Tango.Core;
using Tango.Core.DB;
using Tango.Core.ExtensionMethods;
using Tango.Core.Helpers;
using Tango.Core.IO;
using Tango.Integration.Operation;
+using Tango.Logging;
using Tango.PMR.Synchronization;
using Tango.PPC.Common.Application;
using Tango.PPC.Common.Connection;
@@ -28,6 +31,7 @@ using Tango.Settings;
using Tango.SharedUI.Helpers;
using Tango.SQLExaminer;
using Tango.Transport.Web;
+using System.Data.Entity;
namespace Tango.PPC.Common.MachineSetup
{
@@ -42,6 +46,9 @@ namespace Tango.PPC.Common.MachineSetup
private IUnifiedWriteFilterManager _uwf;
private IOperationSystemManager _windows_manager;
private PPCWebClient _client;
+ private List<LogItemBase> _logs;
+ private bool _isUpdating;
+ private DateTime _setupStartDate;
#region Events
@@ -80,6 +87,21 @@ namespace Tango.PPC.Common.MachineSetup
_remoteAssistance = remoteAssistance;
_uwf = unifiedWriterFilterManager;
_windows_manager = operationSystemManager;
+
+ _logs = new List<LogItemBase>();
+ LogManager.NewLog += LogManager_NewLog;
+ }
+
+ #endregion
+
+ #region Event Handlers
+
+ private void LogManager_NewLog(object sender, LogItemBase e)
+ {
+ if (_isUpdating)
+ {
+ _logs.Add(e);
+ }
}
#endregion
@@ -99,6 +121,82 @@ namespace Tango.PPC.Common.MachineSetup
});
}
+ private async void OnFailed(Exception ex, TaskCompletionSource<MachineSetupResult> completionSource, MachineSetupResponse response)
+ {
+ LogManager.Log(ex, "An error occurred in machine setup.");
+
+ completionSource.SetException(ex);
+
+ if (response != null)
+ {
+ try
+ {
+ var result = await _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
+ {
+ Token = response.NotifyCompletedToken,
+ Status = BL.Enumerations.TangoUpdateStatuses.SetupFailed,
+ FailedReason = ex.FlattenMessage(),
+ FailedLog = GetLogsStringAndClear(),
+ });
+ }
+ catch (Exception xx)
+ {
+ LogManager.Log(xx, "Error notifying setup completed.");
+ }
+ }
+
+ _isUpdating = false;
+ }
+
+ private async void OnCompleted(MachineSetupResult result, TaskCompletionSource<MachineSetupResult> completionSource, MachineSetupResponse response)
+ {
+ completionSource.SetResult(result);
+
+ if (response != null)
+ {
+ try
+ {
+ var r = await _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
+ {
+ Token = response.NotifyCompletedToken,
+ Status = BL.Enumerations.TangoUpdateStatuses.SetupCompleted,
+ });
+ }
+ catch (Exception xx)
+ {
+ LogManager.Log(xx, "Error notifying setup completed.");
+ }
+
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = response.Version;
+ update.FirmwareVersion = response.FirmwareVersion;
+ update.MachineGuid = (await db.Machines.FirstAsync()).Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.SetupCompleted;
+ update.StartDate = _setupStartDate;
+ update.EndDate = DateTime.UtcNow;
+ await db.SaveChangesAsync();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error saving tango setup information to database.");
+ }
+ }
+
+ _isUpdating = false;
+ }
+
+ private String GetLogsStringAndClear()
+ {
+ String logsString = String.Join(Environment.NewLine, _logs.ToList().Select(x => x.ToString()));
+ _logs.Clear();
+ return logsString;
+ }
+
#endregion
#region Public Methods
@@ -111,10 +209,17 @@ namespace Tango.PPC.Common.MachineSetup
/// <returns></returns>
public async Task<MachineSetupResult> Setup(string serialNumber)
{
+ _logs.Clear();
+
TaskCompletionSource<MachineSetupResult> result = new TaskCompletionSource<MachineSetupResult>();
+ MachineSetupResponse setup_response = null;
+ _setupStartDate = DateTime.UtcNow;
+
try
{
+ _isUpdating = true;
+
LogManager.Log($"Starting machine setup for serial number {serialNumber}...");
var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress();
@@ -130,16 +235,24 @@ namespace Tango.PPC.Common.MachineSetup
Login(serialNumber).Wait();
+ String deviceName = $"Tango-{serialNumber}-{settings.DeploymentSlot.ToString()}";
+ LogManager.Log($"Settings device name: '{deviceName}'...");
+ try
+ {
+ await _windows_manager.SetDeviceName(deviceName);
+ }
+ catch
+ {
+ throw new IOException("Error setting device name.");
+ }
+
MachineSetupRequest request = new MachineSetupRequest();
- request.SerialNumber = serialNumber;
request.DeviceID = await _windows_manager.GetDeviceId();
- request.DeviceName = await _windows_manager.GetDeviceName();
-
-
- MachineSetupResponse setup_response = null;
+ request.DeviceName = deviceName;
try
{
+ LogManager.Log($"Sending setup request...\n{request.ToJsonString()}");
setup_response = await _client.MachineSetup(request);
}
catch (Exception ex)
@@ -175,20 +288,20 @@ namespace Tango.PPC.Common.MachineSetup
UpdateProgress("Activating operation system license", "Activating...");
await _windows_manager.Activate(setup_response.OSKey);
}
+ }
- if (setup_response.SetupRemoteAssistance)
- {
- LogManager.Log("Installing remote assistance...");
- UpdateProgress("Installing remote assistance", "Installing...");
- await _remoteAssistance.InstallRemoteAssistance(serialNumber);
- }
+ if (setup_response.SetupRemoteAssistance)
+ {
+ LogManager.Log("Installing remote assistance...");
+ UpdateProgress("Installing remote assistance", "Installing...");
+ await _remoteAssistance.InstallRemoteAssistance(serialNumber, setup_response.Organization, settings.DeploymentSlot.ToString());
+ }
- if (setup_response.SetupUWF)
- {
- LogManager.Log("Activating unified write filter...");
- UpdateProgress("Activating disk protection", "Activating...");
- await _uwf.Setup();
- }
+ if (setup_response.SetupUWF)
+ {
+ LogManager.Log("Activating unified write filter...");
+ UpdateProgress("Activating disk protection", "Activating...");
+ await _uwf.Setup();
}
//Create temporary folders for packages.
@@ -204,27 +317,29 @@ namespace Tango.PPC.Common.MachineSetup
LogManager.Log("Downloading software package...");
- long fileSize = 0;
- UpdateProgress("Downloading software package", "Downloading...", false);
-
- await Task.Factory.StartNew(() =>
+ using (AutoFileDownloader downloader = new AutoFileDownloader(setup_response.BlobAddress, setup_response.CdnAddress, tempFile))
{
- using (FileStreamWrapper fs = new FileStreamWrapper(tempFile.Path, FileMode.Create, (current) =>
+ await downloader.ResolveMode();
+
+ if (downloader.Mode == AutoFileDownloader.DownloadMode.Standard)
{
- UpdateProgress("Downloading software package", "Downloading...", false, current, fileSize);
- }))
+ LogManager.Log($"Connecting to storage CDN with address {downloader.Address}");
+ }
+ else
{
-
- LogManager.Log($"Connecting to storage blob with address {setup_response.BlobAddress}");
- CloudBlockBlob blob = new CloudBlockBlob(new Uri(setup_response.BlobAddress));
- LogManager.Log("Fetching blob attributes...");
- blob.FetchAttributes();
- fileSize = blob.Properties.Length;
- LogManager.Log("Download size: " + fileSize + " bytes.");
- LogManager.Log("Starting blob download...");
- blob.DownloadToStream(fs);
+ LogManager.Log($"Connecting to storage blob with address {downloader.Address}");
}
- });
+
+ downloader.Progress += (x, e) =>
+ {
+ UpdateProgress("Downloading software package", "Downloading...", false, e.Current, e.Total);
+ };
+
+ var size = await downloader.GetFileSize();
+ LogManager.Log("Download size: " + size + " bytes.");
+ LogManager.Log("Starting file download...");
+ await downloader.Download();
+ }
UpdateProgress("Downloading software package", "Extracting package...");
@@ -320,25 +435,25 @@ namespace Tango.PPC.Common.MachineSetup
UpdateProgress("Updating Firmware", "Loading firmware package...");
var tfpPath = Path.Combine(_newPackageTempFolder, "firmware_package.tfp");
var stream = new FileStream(tfpPath, FileMode.Open);
- var handler = await op.UpgradeFirmware(stream);
+ var handler = await op.UpgradeFirmware(stream, setup_response.IsDemo);
handler.Failed += (_, ex) =>
{
stream.Dispose();
- result.SetException(ex);
+ OnFailed(ex, result, setup_response);
};
handler.Completed += (_, __) =>
{
UpdateProgress("Updating Firmware", "Firmware update completed successfully.");
stream.Dispose();
- result.SetResult(new MachineSetupResult()
+ OnCompleted(new MachineSetupResult()
{
UpdatePackagePath = _newPackageTempFolder,
- });
+ }, result, setup_response);
};
handler.Canceled += (_, __) =>
{
stream.Dispose();
- result.SetException(new Exception("The operation has been canceled."));
+ OnFailed(new Exception("The operation has been canceled."), result, setup_response);
};
handler.Progress += (_, e) =>
{
@@ -347,16 +462,15 @@ namespace Tango.PPC.Common.MachineSetup
}
else
{
- result.SetResult(new MachineSetupResult()
+ OnCompleted(new MachineSetupResult()
{
UpdatePackagePath = _newPackageTempFolder,
- });
+ }, result, setup_response);
}
}
catch (Exception ex)
{
- LogManager.Log(ex, "An error occurred in machine setup.");
- result.SetException(ex);
+ OnFailed(ex, result, setup_response);
}
return await result.Task;
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs
index 85d61d4cc..7c835165f 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/IMachineUpdateManager.cs
@@ -4,12 +4,27 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.PMR.Synchronization;
+using Tango.PPC.Common.Publish;
+using Tango.PPC.Common.UpdatePackages;
using Tango.PPC.Common.Web;
namespace Tango.PPC.Common.MachineUpdate
{
public interface IMachineUpdateManager
{
+ /// <summary>
+ /// Occurs when an application update is available.
+ /// </summary>
+ event EventHandler<CheckForUpdateResponse> UpdateAvailable;
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to automatically check for new application updates.
+ /// </summary>
+ bool EnableAutoCheckForUpdates { get; set; }
+
+ /// <summary>
+ /// Gets the current machine update progress status.
+ /// </summary>
MachineUpdateProgress Status { get; }
/// <summary>
@@ -23,46 +38,68 @@ namespace Tango.PPC.Common.MachineUpdate
event EventHandler<MachineUpdateProgress> Progress;
/// <summary>
- /// Performs a machine update using the specified serial number and machine service address.
+ /// Performs a machine update.
/// </summary>
- /// <param name="serialNumber">The serial number.</param>
/// <param name="setupFirmware">if set to <c>true</c> updates the embedded device firmware.</param>
/// <param name="setupFPGA">if set to <c>true</c> updates the embedded device FPGA version and other parameters.</param>
/// <returns></returns>
- Task<MachineUpdateResult> Update(String serialNumber, bool setupFirmware, bool setupFPGA);
+ Task<MachineUpdateResult> Update(bool setupFirmware, bool setupFPGA);
/// <summary>
/// Performs a machine update using the specified software update package path.
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <returns></returns>
- Task<MachineUpdateResult> UpdateFromTUP(String fileName);
+ Task<MachineUpdateResult> UpdateFromTUP(String fileName, bool setupFirmware, bool setupFPGA);
+
+ /// <summary>
+ /// Performs a firmware upgrade from the specified TFP file.
+ /// </summary>
+ /// <param name="fileName">Name of the file.</param>
+ /// <returns></returns>
+ Task UpdateFromTFP(String fileName);
/// <summary>
- /// Checks if any update are available for the specified machine serial number.
+ /// Checks if any update are available.
/// </summary>
- /// <param name="serialNumber">The serial number.</param>
/// <returns></returns>
- Task<CheckForUpdateResponse> CheckForUpdate(String serialNumber);
+ Task<CheckForUpdateResponse> CheckForUpdate();
/// <summary>
/// Checks whether it is necessary to updates all the "overwrite-able" database tables.
/// </summary>
- /// <param name="serialNumber">The serial number.</param>
/// <returns></returns>
- Task<DbCompareResult> UpdateDBCheck(String serialNumber);
+ Task<DbCompareResult> UpdateDBCheck();
/// <summary>
/// Updates all the "overwrite-able" database tables.
/// </summary>
/// <returns></returns>
- Task UpdateDB(DbCompareResult dbCompareResult, String serialNumber);
+ Task UpdateDB(DbCompareResult dbCompareResult);
/// <summary>
/// Gets the update package file information.
/// </summary>
/// <param name="filePath">The file path.</param>
/// <returns></returns>
- Task<UpdatePackageFile> GetUpdatePackageFileInfo(String filePath);
+ Task<PublishInfo> GetUpdatePackageFileInfo(String filePath);
+
+ /// <summary>
+ /// Checks whether any post update packages needs to be installed.
+ /// </summary>
+ /// <returns></returns>
+ Task<bool> PostUpdatePackagesRequired();
+
+ /// <summary>
+ /// Runs all post update packages.
+ /// </summary>
+ /// <returns></returns>
+ Task<PackageRunnerResult> RunPostUpdatePackages();
+
+ /// <summary>
+ /// Restores the last database backup.
+ /// </summary>
+ /// <returns></returns>
+ Task RestoreLastDatabaseBackup();
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs
index b7573ec60..c115f4f5b 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs
@@ -9,6 +9,8 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Tango.BL;
+using Tango.BL.Entities;
using Tango.Core;
using Tango.Core.DB;
using Tango.Core.ExtensionMethods;
@@ -16,26 +18,48 @@ using Tango.Core.Helpers;
using Tango.Core.IO;
using Tango.Integration.Operation;
using Tango.Integration.Upgrade;
+using Tango.Logging;
using Tango.PMR.Synchronization;
using Tango.PPC.Common.Application;
using Tango.PPC.Common.Connection;
+using Tango.PPC.Common.Navigation;
+using Tango.PPC.Common.Publish;
+using Tango.PPC.Common.UpdatePackages;
using Tango.PPC.Common.Web;
using Tango.Settings;
using Tango.SharedUI.Helpers;
using Tango.SQLExaminer;
using Tango.Transport.Web;
+using System.Data.Entity;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.Integration.ExternalBridge;
+using Tango.BL.DTO;
+using Tango.PPC.Shared.Updates;
+using Tango.PPC.Shared.RemoteUpgrade;
+using Tango.Core.Threading;
namespace Tango.PPC.Common.MachineUpdate
{
- public class MachineUpdateManager : ExtendedObject, IMachineUpdateManager
+ public class MachineUpdateManager : ExtendedObject, IMachineUpdateManager, IExternalBridgeRequestHandler
{
private IPPCApplicationManager _app_manager;
private IMachineProvider _machineProvider;
+ private IPackageRunner _packageRunner;
private PPCWebClient _client;
+ private List<LogItemBase> _logs;
+ private System.Timers.Timer _checkForUpdateTimer;
+ private bool _isUpdating;
+ private PPCSettings _settings;
+ private DateTime _updateStartDate;
#region Events
/// <summary>
+ /// Occurs when an application update is available.
+ /// </summary>
+ public event EventHandler<CheckForUpdateResponse> UpdateAvailable;
+
+ /// <summary>
/// Occurs when there is a text log message available.
/// </summary>
public event EventHandler<string> ProgressLog;
@@ -50,12 +74,25 @@ namespace Tango.PPC.Common.MachineUpdate
#region Properties
private MachineUpdateProgress _status;
+ /// <summary>
+ /// Gets the current machine update progress status.
+ /// </summary>
public MachineUpdateProgress Status
{
get { return _status; }
private set { _status = value; RaisePropertyChangedAuto(); }
}
+ private bool _autoCheckForUpdates;
+ /// <summary>
+ /// Gets or sets a value indicating whether to automatically check for new application updates.
+ /// </summary>
+ public bool EnableAutoCheckForUpdates
+ {
+ get { return _autoCheckForUpdates; }
+ set { _autoCheckForUpdates = value; RaisePropertyChangedAuto(); }
+ }
+
#endregion
#region Constructors
@@ -64,23 +101,531 @@ namespace Tango.PPC.Common.MachineUpdate
/// Initializes a new instance of the <see cref="MachineUpdateManager"/> class.
/// </summary>
/// <param name="applicationManager">The application manager.</param>
- public MachineUpdateManager(PPCWebClient ppcWebClient, IPPCApplicationManager applicationManager, IMachineProvider machineProvider)
+ public MachineUpdateManager(PPCWebClient ppcWebClient, IPPCApplicationManager applicationManager, IMachineProvider machineProvider, IPackageRunner packageRunner, IPPCExternalBridgeService externalBridge)
{
_client = ppcWebClient;
_machineProvider = machineProvider;
_app_manager = applicationManager;
+ _app_manager.ApplicationReady += _app_manager_ApplicationReady;
+ _packageRunner = packageRunner;
+ _packageRunner.PackageProgress += _packageRunner_PackageProgress;
+
+ _logs = new List<LogItemBase>();
+ LogManager.NewLog += LogManager_NewLog;
+
+ _settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+
+ _checkForUpdateTimer = new System.Timers.Timer(_settings.AutoUpdateCheckInterval.TotalMilliseconds);
+ _checkForUpdateTimer.Elapsed += _checkForUpdateTimer_Elapsed;
+ _checkForUpdateTimer.Stop();
+
+ externalBridge.RegisterRequestHandler(this);
+ }
+
+ #endregion
+
+ #region Event Handlers
+
+ private void _app_manager_ApplicationReady(object sender, EventArgs e)
+ {
+ _checkForUpdateTimer.Start();
+
+ if (!_app_manager.IsUpdateFailed)
+ {
+ ClearLastDatabaseBackup();
+ }
+ }
+
+ private void _packageRunner_PackageProgress(object sender, PackageProgressEventArgs e)
+ {
+ UpdateProgress(e.PackageName, e.Message, e.IsIntermediate, e.Progress, e.Total);
+ }
+
+ private void LogManager_NewLog(object sender, LogItemBase e)
+ {
+ if (_isUpdating)
+ {
+ _logs.Add(e);
+ }
}
#endregion
#region Private Methods
- private Task Login(String serialNumber)
+ private Task Login(String machineGuid)
{
return _client.Login(new LoginRequest()
{
Mode = LoginMode.Machine,
- SerialNumber = serialNumber,
+ MachineGuid = machineGuid,
+ });
+ }
+
+ private async void OnFailed(Exception ex, TaskCompletionSource<MachineUpdateResult> completionSource, DownloadUpdateResponse response, bool performDatabaseRollback, String dbBackupFile, String backupsFolder, String tempDbName, Tango.Core.DataSource localDataSource, String tempUpdatePackageFolder = null, PublishInfo tupPublishInfo = null)
+ {
+ LogManager.Log(ex, "An error occurred in machine update.");
+
+ await Task.Factory.StartNew(() =>
+ {
+
+ if (performDatabaseRollback)
+ {
+ LogManager.Log("Rolling back database changes...");
+
+ using (DbManager db = DbManager.FromDataSource(localDataSource))
+ {
+ try
+ {
+ UpdateProgress("Rollback", "Rolling back database changes...");
+ db.Restore(localDataSource.Catalog, dbBackupFile);
+ LogManager.Log("Database restored successfully.");
+ }
+ catch (Exception e)
+ {
+ LogManager.Log(e, "Could not rollback the database.");
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(dbBackupFile);
+ }
+ catch { }
+ }
+ }
+ }
+
+ if (tempDbName != null)
+ {
+ try
+ {
+ LogManager.Log($"Removing temporary database '{tempDbName}'...");
+ using (DbManager dbManager = DbManager.FromDataSource(localDataSource))
+ {
+ dbManager.SetOffline(tempDbName);
+ dbManager.SetOnline(tempDbName);
+ dbManager.Delete(tempDbName);
+ }
+ }
+ catch (Exception exx)
+ {
+ LogManager.Log(exx, "Error removing temporary database.");
+ }
+ }
+
+ try
+ {
+ Directory.Delete(backupsFolder, true);
+ }
+ catch (Exception ee)
+ {
+ LogManager.Log(ee, $"Error deleting backups folder '{backupsFolder}'.");
+ }
+
+ if (tempUpdatePackageFolder != null)
+ {
+ try
+ {
+ Directory.Delete(tempUpdatePackageFolder, true);
+ }
+ catch (Exception eee)
+ {
+ LogManager.Log(eee, "Error removing temporary package folder.");
+ }
+ }
+
+ });
+
+ completionSource.SetException(ex);
+
+ String logs = GetLogsStringAndClear();
+
+ if (response != null)
+ {
+ try
+ {
+ var result = await _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
+ {
+ Token = response.NotifyCompletedToken,
+ Status = BL.Enumerations.TangoUpdateStatuses.UpdateFailed,
+ FailedReason = ex.FlattenMessage(),
+ FailedLog = logs,
+ });
+ }
+ catch (Exception xx)
+ {
+ LogManager.Log(xx, "Error notifying update failed.");
+ }
+
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = response.Version;
+ update.FirmwareVersion = response.FirmwareVersion;
+ update.MachineGuid = _machineProvider.Machine.Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.UpdateFailed;
+ update.StartDate = _updateStartDate;
+ update.EndDate = DateTime.UtcNow;
+ update.FailedReason = ex.FlattenMessage();
+ update.FailedLog = logs;
+ db.TangoUpdates.Add(update);
+ await db.SaveChangesAsync();
+ }
+ }
+ catch (Exception xxx)
+ {
+ LogManager.Log(xxx, "Error saving tango update information to database.");
+ }
+ }
+
+
+ if (tupPublishInfo != null)
+ {
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = tupPublishInfo.ApplicationVersion;
+ update.FirmwareVersion = tupPublishInfo.GetFirmwareVersion();
+ update.MachineGuid = _machineProvider.Machine.Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.OfflineUpdateFailed;
+ update.StartDate = _updateStartDate;
+ update.EndDate = DateTime.UtcNow;
+ update.FailedReason = ex.FlattenMessage();
+ update.FailedLog = logs;
+ db.TangoUpdates.Add(update);
+ await db.SaveChangesAsync();
+ }
+ }
+ catch (Exception xxx)
+ {
+ LogManager.Log(xxx, "Error saving tango offline update information to database.");
+ }
+ }
+
+ _isUpdating = false;
+ }
+
+ private async void OnCompleted(MachineUpdateResult result, TaskCompletionSource<MachineUpdateResult> completionSource, DownloadUpdateResponse response, String tempDbName, String backupsFolder, Core.DataSource localDataSource, PublishInfo tupPublishInfo = null)
+ {
+ await Task.Factory.StartNew(() =>
+ {
+ if (tempDbName != null)
+ {
+ try
+ {
+ LogManager.Log($"Removing temporary database '{tempDbName}'...");
+ using (DbManager dbManager = DbManager.FromDataSource(localDataSource))
+ {
+ dbManager.SetOffline(tempDbName);
+ dbManager.SetOnline(tempDbName);
+ dbManager.Delete(tempDbName);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error removing temporary database.");
+ }
+ }
+
+ //try
+ //{
+ // Directory.Delete(backupsFolder, true);
+ //}
+ //catch (Exception ex)
+ //{
+ // LogManager.Log(ex, $"Error deleting backups folder '{backupsFolder}'.");
+ //}
+
+ if (!result.RequiresBinariesUpdate)
+ {
+ try
+ {
+ Directory.Delete(result.UpdatePackagePath, true);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error removing temporary package folder.");
+ }
+ }
+
+ });
+
+ completionSource.SetResult(result);
+
+ if (response != null)
+ {
+ try
+ {
+ var r = await _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
+ {
+ Token = response.NotifyCompletedToken,
+ Status = BL.Enumerations.TangoUpdateStatuses.UpdateCompleted,
+ });
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error notifying update completed.");
+ }
+
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = response.Version;
+ update.FirmwareVersion = response.FirmwareVersion;
+ update.MachineGuid = (await db.Machines.FirstAsync()).Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.UpdateCompleted;
+ update.StartDate = _updateStartDate;
+ update.EndDate = DateTime.UtcNow;
+ db.TangoUpdates.Add(update);
+ await db.SaveChangesAsync();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error saving tango update information to database.");
+ }
+ }
+
+
+ if (tupPublishInfo != null)
+ {
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = tupPublishInfo.ApplicationVersion;
+ update.FirmwareVersion = tupPublishInfo.GetFirmwareVersion();
+ update.MachineGuid = _machineProvider.Machine.Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.OfflineUpdateCompleted;
+ update.StartDate = _updateStartDate;
+ update.EndDate = DateTime.UtcNow;
+ db.TangoUpdates.Add(update);
+ await db.SaveChangesAsync();
+ }
+ }
+ catch (Exception xxx)
+ {
+ LogManager.Log(xxx, "Error saving tango offline update information to database.");
+ }
+ }
+
+ _isUpdating = false;
+ }
+
+ private void OnFailed(Exception ex, UpdateDBResponse response, bool performDatabaseRollback, String dbBackupFile, Tango.Core.DataSource localDataSource)
+ {
+ LogManager.Log(ex, "An error occurred in database update.");
+
+ if (performDatabaseRollback)
+ {
+ LogManager.Log("Rolling back database changes...");
+
+ using (DbManager db = DbManager.FromDataSource(localDataSource))
+ {
+ try
+ {
+ UpdateProgress("Rollback", "Rolling back database changes...");
+ db.Restore(localDataSource.Catalog, dbBackupFile);
+ LogManager.Log("Database restored successfully.");
+ }
+ catch (Exception e)
+ {
+ LogManager.Log(e, "Could not rollback the database.");
+ throw ex;
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(dbBackupFile);
+ }
+ catch { }
+ }
+ }
+ }
+
+ String logs = GetLogsStringAndClear();
+
+ if (response != null)
+ {
+ try
+ {
+ var r = _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
+ {
+ Token = response.NotifyCompletedToken,
+ Status = BL.Enumerations.TangoUpdateStatuses.DatabaseFailed,
+ FailedReason = ex.FlattenMessage(),
+ FailedLog = logs,
+ }).Result;
+ }
+ catch (Exception xx)
+ {
+ LogManager.Log(xx, "Error notifying database failed.");
+ }
+
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = _app_manager.Version.ToString();
+ update.FirmwareVersion = _app_manager.FirmwareVersion.ToString();
+ update.MachineGuid = _machineProvider.Machine.Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.DatabaseFailed;
+ update.StartDate = _updateStartDate;
+ update.EndDate = DateTime.UtcNow;
+ update.FailedReason = ex.FlattenMessage();
+ update.FailedLog = logs;
+ db.TangoUpdates.Add(update);
+ db.SaveChanges();
+ }
+ }
+ catch (Exception exx)
+ {
+ LogManager.Log(exx, "Error saving database update information to database.");
+ }
+ }
+
+ _isUpdating = false;
+ }
+
+ private void OnCompleted(UpdateDBResponse response, bool completedWithNoDifferences = false)
+ {
+ if (response != null)
+ {
+ try
+ {
+ var r = _client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
+ {
+ Token = response.NotifyCompletedToken,
+ Status = BL.Enumerations.TangoUpdateStatuses.DatabaseCompleted,
+ ReportsAboutDbCheckNoDifferences = completedWithNoDifferences,
+ }).Result;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error notifying database completed.");
+ }
+
+ if (!completedWithNoDifferences)
+ {
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = _app_manager.Version.ToString();
+ update.FirmwareVersion = _app_manager.FirmwareVersion.ToString();
+ update.MachineGuid = _machineProvider.Machine.Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.DatabaseCompleted;
+ update.StartDate = _updateStartDate;
+ update.EndDate = DateTime.UtcNow;
+ db.TangoUpdates.Add(update);
+ db.SaveChanges();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error saving database update information to database.");
+ }
+ }
+ }
+
+ _isUpdating = false;
+ }
+
+ private void OnFailed(Exception ex, TaskCompletionSource<object> completionSource, String firmwareVersion)
+ {
+ LogManager.Log(ex, "An error occurred in firmware upgrade.");
+
+ completionSource.SetException(ex);
+ String logs = GetLogsStringAndClear();
+
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = _app_manager.Version.ToString();
+ update.FirmwareVersion = firmwareVersion;
+ update.MachineGuid = _machineProvider.Machine.Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.OfflineFirmwareUpgradeFailed;
+ update.StartDate = _updateStartDate;
+ update.EndDate = DateTime.UtcNow;
+ update.FailedReason = ex.FlattenMessage();
+ update.FailedLog = logs;
+ db.TangoUpdates.Add(update);
+ db.SaveChanges();
+ }
+ }
+ catch (Exception exx)
+ {
+ LogManager.Log(exx, "Error saving firmware upgrade information to database.");
+ }
+
+ _isUpdating = false;
+ }
+
+ private void OnCompleted(TaskCompletionSource<object> completionSource, String firmwareVersion)
+ {
+ LogManager.Log("Firmware upgrade completed successfully.");
+ completionSource.SetResult(true);
+
+ try
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ TangoUpdate update = new TangoUpdate();
+ update.ApplicationVersion = _app_manager.Version.ToString();
+ update.FirmwareVersion = firmwareVersion;
+ update.MachineGuid = _machineProvider.Machine.Guid;
+ update.UpdateStatus = BL.Enumerations.TangoUpdateStatuses.OfflineFirmwareUpgradeCompleted;
+ update.StartDate = _updateStartDate;
+ update.EndDate = DateTime.UtcNow;
+ db.TangoUpdates.Add(update);
+ db.SaveChanges();
+ }
+ }
+ catch (Exception exx)
+ {
+ LogManager.Log(exx, "Error saving firmware upgrade information to database.");
+ }
+
+ _isUpdating = false;
+ }
+
+ private String GetLogsStringAndClear()
+ {
+ String logsString = String.Join(Environment.NewLine, _logs.ToList().Select(x => x.ToString()));
+ _logs.Clear();
+ return logsString;
+ }
+
+ private void ClearLastDatabaseBackup()
+ {
+ Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ var lastBackupFile = SettingsManager.Default.GetOrCreate<PPCSettings>().LastDatabaseBackupFile;
+
+ if (File.Exists(lastBackupFile))
+ {
+ File.Delete(lastBackupFile);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error removing last database backup file.");
+ }
});
}
@@ -89,9 +634,8 @@ namespace Tango.PPC.Common.MachineUpdate
#region Public Methods
/// <summary>
- /// Performs a machine update using the specified serial number and machine service address.
+ /// Performs a machine update.
/// </summary>
- /// <param name="serialNumber">The serial number.</param>
/// <param name="setupFirmware">if set to <c>true</c> updates the embedded device firmware.</param>
/// <param name="setupFPGA">if set to <c>true</c> updates the embedded device FPGA version and other parameters.</param>
/// <returns></returns>
@@ -101,19 +645,32 @@ namespace Tango.PPC.Common.MachineUpdate
/// or
/// </exception>
/// <exception cref="System.InvalidProgramException">Database tango does not exists.</exception>
- public async Task<MachineUpdateResult> Update(String serialNumber, bool setupFirmware, bool setupFPGA)
+ public async Task<MachineUpdateResult> Update(bool setupFirmware, bool setupFPGA)
{
+ _updateStartDate = DateTime.UtcNow;
+ _logs.Clear();
+
TaskCompletionSource<MachineUpdateResult> result = new TaskCompletionSource<MachineUpdateResult>();
var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;
bool performDatabaseRollback = false;
String dbBackupFile = null;
+ DownloadUpdateResponse update_response = null;
+ String backupsFolder = "C:\\Backups";
+
+ //Create temporary folders for packages.
+ var _newPackageTempFolder = TemporaryManager.CreateFolder();
+ _newPackageTempFolder.Persist = true;
+
+ String machineGuid = _machineProvider.Machine.Guid;
try
{
- var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress();
+ _isUpdating = true;
+
+ var machineServiceAddress = _settings.GetMachineServiceAddress();
- LogManager.Log($"Starting machine update for serial number {serialNumber}...");
+ LogManager.Log($"Starting machine update...");
//Connecting to machine...
LogManager.Log("Verifying machine connection and state...");
@@ -132,10 +689,11 @@ namespace Tango.PPC.Common.MachineUpdate
{
throw LogManager.Log(new InvalidOperationException("Could not perform an update while the machine is not connected."));
}
- if (op.IsPrinting)
- {
- throw LogManager.Log(new InvalidOperationException($"Could not perform an update while the machine is in {op.Status} status."));
- }
+ }
+
+ if (!op.CanPrint)
+ {
+ throw LogManager.Log(new InvalidOperationException($"Could not perform an update while the machine is in {op.Status} status."));
}
//Connect to machine service and get matching packages for this machine.
@@ -143,21 +701,14 @@ namespace Tango.PPC.Common.MachineUpdate
LogManager.Log($"Connecting to machine service on {machineServiceAddress}...");
- await Login(serialNumber);
+ await Login(machineGuid);
DownloadUpdateRequest request = new DownloadUpdateRequest();
- request.SerialNumber = serialNumber;
-
- DownloadUpdateResponse update_response = null;
update_response = await _client.MachineUpdate(request);
LogManager.Log($"Machine update response received: {Environment.NewLine}{update_response.ToJsonString()}");
- //Create temporary folders for packages.
- var _newPackageTempFolder = TemporaryManager.CreateFolder();
- _newPackageTempFolder.Persist = true;
-
LogManager.Log($"Temporary package folder created: {_newPackageTempFolder}.");
//Download software package.
@@ -167,68 +718,102 @@ namespace Tango.PPC.Common.MachineUpdate
LogManager.Log("Downloading software package...");
- long fileSize = 0;
UpdateProgress("Downloading software package", "Downloading...", false);
- using (FileStreamWrapper fs = new FileStreamWrapper(tempFile.Path, FileMode.Create, (current) =>
+ using (AutoFileDownloader downloader = new AutoFileDownloader(update_response.BlobAddress, update_response.CdnAddress, tempFile))
{
- UpdateProgress("Downloading software package", "Downloading...", false, current, fileSize);
- }))
- {
- LogManager.Log($"Connecting to storage blob with address {update_response.BlobAddress}");
- CloudBlockBlob blob = new CloudBlockBlob(new Uri(update_response.BlobAddress));
- LogManager.Log("Fetching blob attributes...");
- blob.FetchAttributes();
- fileSize = blob.Properties.Length;
- LogManager.Log("Download size: " + fileSize + " bytes.");
- LogManager.Log("Starting blob download...");
- blob.DownloadToStream(fs);
+ await downloader.ResolveMode();
+
+ if (downloader.Mode == AutoFileDownloader.DownloadMode.Standard)
+ {
+ LogManager.Log($"Connecting to storage CDN with address {downloader.Address}");
+ }
+ else
+ {
+ LogManager.Log($"Connecting to storage blob with address {downloader.Address}");
+ }
+
+ downloader.Progress += (x, e) =>
+ {
+ UpdateProgress("Downloading software package", "Downloading...", false, e.Current, e.Total);
+ };
+
+ var size = await downloader.GetFileSize();
+ LogManager.Log("Download size: " + size + " bytes.");
+ LogManager.Log("Starting file download...");
+ await downloader.Download();
}
UpdateProgress("Downloading software package", "Extracting package...");
LogManager.Log("Extracting downloaded zip file...");
- //Extract software package.
- ZipFile.ExtractToDirectory(tempFile, _newPackageTempFolder);
+ await Task.Factory.StartNew(() =>
+ {
+ //Extract software package.
+ ZipFile.ExtractToDirectory(tempFile, _newPackageTempFolder);
+ });
LogManager.Log("Copying latest updater utility to application path...");
//Copy new updater utility to app path.
File.Copy(Path.Combine(_newPackageTempFolder, "Tango.PPC.Updater.exe"), Path.Combine(PathHelper.GetStartupPath(), "Tango.PPC.Updater.exe"), true);
-
- //Synchronize database
- UpdateProgress("Updating Database", "Initializing...");
-
- LogManager.Log($"Synchronizing database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'...");
-
- UpdateProgress("Updating Database", "Connecting to local database...");
LogManager.Log("Initializing database manager...");
DbManager db = DbManager.FromDataSource(localDataSource);
- LogManager.Log("Checking Tango database exists on the local machine...");
- if (!db.Exists(localDataSource.Catalog))
+ //Create Database Backup
+ UpdateProgress("Updating Database", "Creating database backup...");
+ try
{
- throw new InvalidProgramException("Database tango does not exists.");
+ Directory.CreateDirectory(backupsFolder);
+ dbBackupFile = $"{backupsFolder}\\{Path.GetRandomFileName()}.bak";
+ _settings.LastDatabaseBackupFile = dbBackupFile;
+ _settings.Save();
+ LogManager.Log($"Creating database backup to '{dbBackupFile}'...");
+ await Task.Factory.StartNew(() => db.Backup(localDataSource.Catalog, dbBackupFile));
+ performDatabaseRollback = true;
+ LogManager.Log("Database backup created successfully.");
+ }
+ catch (Exception ex)
+ {
+ throw LogManager.Log(ex, "Update manager error while trying to create a database backup.");
}
- if (setupFirmware)
+ //Run pre-update packages.
+ try
{
- LogManager.Log("Setup firmware is active so a database rollback procedure should be configured.");
- UpdateProgress("Updating Database", "Creating database backup...");
+ UpdateProgress("Preparing", "Running update packages...");
+ LogManager.Log("Running pre-update packages...");
+ var packagesFolder = Path.Combine(_newPackageTempFolder, "Packages");
+ Version updateVersion = new Version(1, 0, 0, 0);
try
{
- Directory.CreateDirectory("C:\\Backups");
- dbBackupFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak";
- LogManager.Log($"Creating database backup to '{dbBackupFile}'...");
- await Task.Factory.StartNew(() => db.Backup(localDataSource.Catalog, dbBackupFile));
- LogManager.Log("Database backup created successfully.");
+ updateVersion = Version.Parse(update_response.Version);
}
catch (Exception ex)
{
- throw LogManager.Log(ex, "Setup manager error while trying to create a database backup.");
+ LogManager.Log(ex, "Error parsing new version string for package runner.");
}
+
+ await _packageRunner.Run(PackageType.Pre, updateVersion, packagesFolder);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error running pre-update packages...");
+ }
+
+ //Synchronize database
+ UpdateProgress("Updating Database", "Initializing...");
+
+ LogManager.Log($"Synchronizing database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'...");
+
+ UpdateProgress("Updating Database", "Connecting to local database...");
+
+ LogManager.Log("Checking Tango database exists on the local machine...");
+ if (!db.Exists(localDataSource.Catalog))
+ {
+ throw new InvalidProgramException("Database tango does not exists.");
}
LogManager.Log("Disposing database manager.");
@@ -243,7 +828,7 @@ namespace Tango.PPC.Common.MachineUpdate
Path.Combine(_newPackageTempFolder, "Update Scripts"),
update_response.DataSource,
localDataSource,
- serialNumber);
+ machineGuid);
runner.Log += (x, msg) =>
{
@@ -267,14 +852,12 @@ namespace Tango.PPC.Common.MachineUpdate
}
catch (Exception ex)
{
- throw LogManager.Log(ex, "Setup manager error while trying to synchronize database.");
+ throw LogManager.Log(ex, "Update manager error while trying to synchronize database.");
}
//Updating firmware
if (setupFirmware)
{
- performDatabaseRollback = true;
-
UpdateProgress("Updating Firmware", "Connecting to firmware device...");
LogManager.Log("");
LogManager.Log("-------------------------------------------------------------------------");
@@ -293,106 +876,88 @@ namespace Tango.PPC.Common.MachineUpdate
op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU;
}
- var handler = await op.UpgradeFirmware(stream);
+ var handler = await op.UpgradeFirmware(stream, _machineProvider.Machine.IsDemo);
handler.Failed += (_, ex) =>
{
stream.Dispose();
- throw ex;
+ OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder);
};
handler.Completed += (_, __) =>
{
UpdateProgress("Updating Firmware", "Firmware update completed successfully.");
stream.Dispose();
- result.SetResult(new MachineUpdateResult()
+ OnCompleted(new MachineUpdateResult()
{
UpdatePackagePath = _newPackageTempFolder,
- });
+ }, result, update_response, null, backupsFolder, localDataSource);
};
handler.Canceled += (_, __) =>
{
stream.Dispose();
- throw new Exception("The operation has been canceled.");
+ OnFailed(new Exception("The operation has been canceled."), result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder);
};
handler.Progress += (_, e) =>
{
- UpdateProgress("Updating Firmware", e.Message, false, e.Current, e.Total);
+ UpdateProgress("Updating Firmware", e.Message, e.IsIndeterminate, e.Current, e.Total);
};
}
else
{
- result.SetResult(new MachineUpdateResult()
+ OnCompleted(new MachineUpdateResult()
{
UpdatePackagePath = _newPackageTempFolder,
- });
+ }, result, update_response, null, backupsFolder, localDataSource);
}
}
catch (Exception ex)
{
- LogManager.Log(ex, "An error occurred in machine update.");
-
- if (performDatabaseRollback)
- {
- LogManager.Log("Rolling back database changes...");
-
- using (DbManager db = DbManager.FromDataSource(localDataSource))
- {
- try
- {
- UpdateProgress("Rollback", "Rolling back database changes...");
- await Task.Factory.StartNew(() => db.Restore(localDataSource.Catalog, dbBackupFile));
- LogManager.Log("Database restored successfully.");
- }
- catch (Exception e)
- {
- LogManager.Log(e, "Could not rollback the database.");
- throw ex;
- }
- finally
- {
- try
- {
- File.Delete(dbBackupFile);
- }
- catch { }
- }
- }
- }
-
- result.SetException(ex);
- }
- finally
- {
- try
- {
- File.Delete(dbBackupFile);
- }
- catch { }
+ OnFailed(ex, result, update_response, performDatabaseRollback, dbBackupFile, backupsFolder, null, localDataSource, _newPackageTempFolder);
}
return await result.Task;
}
/// <summary>
- /// Checks if any update are available for the specified machine serial number.
+ /// Checks if any update are available for the specified machine.
/// </summary>
- /// <param name="serialNumber">The serial number.</param>
/// <param name="machineServiceAddress">The machine service address.</param>
/// <returns></returns>
- public Task<CheckForUpdateResponse> CheckForUpdate(string serialNumber)
+ public Task<CheckForUpdateResponse> CheckForUpdate()
{
return Task.Factory.StartNew<CheckForUpdateResponse>(() =>
{
- var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress();
+ _isUpdating = true;
+
+ var machineServiceAddress = _settings.GetMachineServiceAddress();
LogManager.Log($"Connecting to machine service on {machineServiceAddress}...");
- Login(serialNumber).GetAwaiter().GetResult();
+ String machineGuid = _machineProvider.Machine.Guid;
+
+ Login(machineGuid).GetAwaiter().GetResult();
LogManager.Log($"Checking if updates available...");
CheckForUpdateRequest request = new CheckForUpdateRequest();
- request.SerialNumber = serialNumber;
request.Version = _app_manager.Version.ToString();
+ request.FirmwareVersion = _app_manager.FirmwareVersion?.ToString();
+
+ try
+ {
+ request.MachineLastUpdated = _machineProvider.Machine.LastUpdated;
+
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ request.Rmls = db.Rmls.ToList().Select(x => new UpdatedEntity(x)).ToList();
+ request.HardwareVersions = db.HardwareVersions.ToList().Select(x => new UpdatedEntity(x)).ToList();
+ request.Catalogs = db.ColorCatalogs.ToList().Select(x => new UpdatedEntity(x)).ToList();
+ request.UsedRmlsGuids = db.Jobs.Select(x => x.RmlGuid).Distinct().ToList();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "An error occurred while trying to fill the existing database entities before checking for updates.");
+ }
CheckForUpdateResponse update_response = null;
@@ -400,6 +965,8 @@ namespace Tango.PPC.Common.MachineUpdate
LogManager.Log($"Check for update response received: {Environment.NewLine}{update_response.ToJsonString()}");
+ _isUpdating = false;
+
return update_response;
});
}
@@ -407,96 +974,145 @@ namespace Tango.PPC.Common.MachineUpdate
/// <summary>
/// Updates all the "overwrite-able" database tables.
/// </summary>
- /// <param name="serialNumber">The serial number.</param>
/// <param name="machineServiceAddress">The machine service address.</param>
/// <returns></returns>
- public Task UpdateDB(DbCompareResult dbCompareResult, String serialNumber)
+ public Task UpdateDB(DbCompareResult dbCompareResult)
{
+ _updateStartDate = DateTime.UtcNow;
+ _logs.Clear();
+
return Task.Factory.StartNew(() =>
{
- LogManager.Log("Starting database update...");
+ _isUpdating = true;
+ UpdateDBResponse update_response = null;
+ var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;
+ bool performDatabaseRollback = false;
+ String dbBackupFile = null;
- UpdateProgress("Updating Database", "Initializing...");
+ try
+ {
+ LogManager.Log("Starting database update...");
- LogManager.Log("Looking for update scripts configuration on application path...");
+ if (_machineProvider.MachineOperator.IsPrinting)
+ {
+ throw LogManager.Log(new InvalidOperationException($"Could not perform a database update while the machine is dyeing."));
+ }
- String config_file = Path.Combine(PathHelper.GetStartupPath(), "Update Scripts", "config.xml");
+ UpdateProgress("Updating Database", "Initializing...");
- if (!File.Exists(config_file))
- {
- throw LogManager.Log(new FileNotFoundException($"Could not locate '{config_file}' file on application folder."));
- }
+ LogManager.Log("Looking for update scripts configuration on application path...");
- UpdateDBResponse update_response = dbCompareResult.UpdateDBResponse;
+ String config_file = Path.Combine(PathHelper.GetStartupPath(), "Update Scripts", "config.xml");
- var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;
+ if (!File.Exists(config_file))
+ {
+ throw LogManager.Log(new FileNotFoundException($"Could not locate '{config_file}' file on application folder."));
+ }
- LogManager.Log($"Updating database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'...");
+ update_response = dbCompareResult.UpdateDBResponse;
- UpdateProgress("Updating Database", "Initializing update sequence...");
+ LogManager.Log($"Updating database '{update_response.DataSource.ToString()}' => '{localDataSource.ToString()}'...");
- ExaminerSequenceConfiguration config_sequence = ExaminerSequenceConfiguration.FromFile(config_file);
+ UpdateProgress("Updating Database", "Initializing update sequence...");
- foreach (var item in config_sequence.Items.Where(x => x.Type == ExaminerSequenceItemType.Data || update_response.PerformSchemaUpdate).OrderBy(x => x.Index))
- {
- LogManager.Log($"Executing update script '{item.FileName}...'");
+ ExaminerSequenceConfiguration config_sequence = ExaminerSequenceConfiguration.FromFile(config_file);
- ExaminerConfigurationBuilder builder = new ExaminerConfigurationBuilder(Path.Combine(Path.GetDirectoryName(config_file), item.FileName));
- builder.SetSource(update_response.DataSource);
- builder.SetTarget(localDataSource);
+ UpdateProgress("Updating Database", "Connecting to local database...");
+ LogManager.Log("Initializing database manager...");
+ DbManager db = DbManager.FromDataSource(localDataSource);
- if (item.RequiresSerialNumber)
+ LogManager.Log("Checking Tango database exists on the local machine...");
+ if (!db.Exists(localDataSource.Catalog))
{
- builder.SetMachineSerialNumber(serialNumber);
+ throw new InvalidProgramException("Database tango does not exists.");
}
- builder.Synchronize();
-
- var config = builder.Build();
+ UpdateProgress("Updating Database", "Creating database backup...");
- ExaminerProcess process = new ExaminerProcess(config, item.Type == ExaminerSequenceItemType.Data ? ExaminerProcessType.Data : ExaminerProcessType.Schema);
- process.Progress += (x, msg) =>
+ //Create Database Backup
+ try
{
- LogManager.Log(msg);
- };
+ Directory.CreateDirectory("C:\\Backups");
+ dbBackupFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak";
+ LogManager.Log($"Creating database backup to '{dbBackupFile}'...");
+ db.Backup(localDataSource.Catalog, dbBackupFile);
+ performDatabaseRollback = true;
+ LogManager.Log("Database backup created successfully.");
+ }
+ catch (Exception ex)
+ {
+ throw LogManager.Log(ex, "Update manager error while trying to create a database backup.");
+ }
- try
+ LogManager.Log("Disposing database manager.");
+ db.Dispose();
+
+ foreach (var item in config_sequence.Items.Where(x => x.Type == ExaminerSequenceItemType.Data || update_response.PerformSchemaUpdate).OrderBy(x => x.Index))
{
- UpdateProgress("Updating Database", item.Name + "...");
+ LogManager.Log($"Executing update script '{item.FileName}...'");
- var result = process.Execute().Result;
+ ExaminerConfigurationBuilder builder = new ExaminerConfigurationBuilder(Path.Combine(Path.GetDirectoryName(config_file), item.FileName));
+ builder.SetSource(update_response.DataSource);
+ builder.SetTarget(localDataSource);
- if (result.ExitCode != ExaminerProcessExitCode.Success)
+ if (item.RequiresSerialNumber)
{
- throw LogManager.Log(new InvalidDataException($"{item.FileName} script has terminated with exit code '{result.ExitCode}'."));
+ builder.SetMachineSerialNumber(_machineProvider.Machine.Guid);
}
- LogManager.Log("Script executed successfully.");
- }
- catch (Exception ex)
- {
- throw LogManager.Log(ex, "Setup manager error while trying to update the database.");
+ builder.Synchronize();
+
+ var config = builder.Build();
+
+ ExaminerProcess process = new ExaminerProcess(config, item.Type == ExaminerSequenceItemType.Data ? ExaminerProcessType.Data : ExaminerProcessType.Schema);
+ process.Progress += (x, msg) =>
+ {
+ LogManager.Log(msg);
+ };
+
+ try
+ {
+ UpdateProgress("Updating Database", item.Name + "...");
+
+ var result = process.Execute().Result;
+
+ if (result.ExitCode != ExaminerProcessExitCode.Success)
+ {
+ throw LogManager.Log(new InvalidDataException($"{item.FileName} script has terminated with exit code '{result.ExitCode}'."));
+ }
+
+ LogManager.Log("Script executed successfully.");
+ }
+ catch (Exception ex)
+ {
+ throw LogManager.Log(ex, "Upudate manager error while trying to update the database.");
+ }
}
- }
- UpdateProgress("Updating Database", "Database synchronization completed successfully.");
- LogManager.Log("Update completed successfully.");
+ UpdateProgress("Updating Database", "Database synchronization completed successfully.");
+ LogManager.Log("Update completed successfully.");
+ OnCompleted(update_response);
+ }
+ catch (Exception ex)
+ {
+ OnFailed(ex, update_response, performDatabaseRollback, dbBackupFile, localDataSource);
+ throw ex;
+ }
});
}
/// <summary>
/// Checks whether it is necessary to updates all the "overwrite-able" database tables.
/// </summary>
- /// <param name="serialNumber">The serial number.</param>
/// <param name="machineServiceAddress">The machine service address.</param>
/// <returns></returns>
- public Task<DbCompareResult> UpdateDBCheck(string serialNumber)
+ public Task<DbCompareResult> UpdateDBCheck()
{
return Task.Factory.StartNew<DbCompareResult>(() =>
{
- var machineServiceAddress = SettingsManager.Default.GetOrCreate<PPCSettings>().GetMachineServiceAddress();
+ var machineServiceAddress = _settings.GetMachineServiceAddress();
- LogManager.Log($"Checking if database update is required for serial number {serialNumber}...");
+ LogManager.Log($"Checking if database update is required...");
LogManager.Log("Looking for update scripts configuration on application path...");
@@ -509,10 +1125,11 @@ namespace Tango.PPC.Common.MachineUpdate
LogManager.Log($"Connecting to machine service on {machineServiceAddress}...");
- Login(serialNumber).Wait();
+ Login(_machineProvider.Machine.Guid).Wait();
UpdateDBRequest request = new UpdateDBRequest();
- request.SerialNumber = serialNumber;
+ request.ApplicationVersion = _app_manager.Version.ToString();
+ request.FirmwareVersion = _app_manager.FirmwareVersion.ToString();
UpdateDBResponse update_response = null;
@@ -541,7 +1158,7 @@ namespace Tango.PPC.Common.MachineUpdate
if (item.RequiresSerialNumber)
{
- builder.SetMachineSerialNumber(serialNumber);
+ builder.SetMachineSerialNumber(_machineProvider.Machine.Guid);
}
var config = builder.Build();
@@ -580,12 +1197,18 @@ namespace Tango.PPC.Common.MachineUpdate
}
catch (Exception ex)
{
+ OnFailed(ex, update_response, false, null, null);
throw LogManager.Log(ex, "Update manager error while trying to compare the database.");
}
}
LogManager.Log("Comparison completed successfully.");
+ if (!has_differences)
+ {
+ OnCompleted(update_response, true);
+ }
+
return new DbCompareResult()
{
RequiresUpdate = has_differences,
@@ -599,31 +1222,373 @@ namespace Tango.PPC.Common.MachineUpdate
/// </summary>
/// <param name="fileName">Name of the file.</param>
/// <returns></returns>
- public Task<MachineUpdateResult> UpdateFromTUP(string fileName)
+ public async Task<MachineUpdateResult> UpdateFromTUP(string fileName, bool setupFirmware, bool setupFPGA)
{
- return Task.Factory.StartNew<MachineUpdateResult>(() =>
+ _updateStartDate = DateTime.UtcNow;
+ _logs.Clear();
+
+ TaskCompletionSource<MachineUpdateResult> result = new TaskCompletionSource<MachineUpdateResult>();
+
+ var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;
+ bool performDatabaseRollback = false;
+ String dbBackupFile = null;
+ String tempDbName = "Tango_TUP";
+ String tempDbFileName = tempDbName + ".bak";
+ String backupsFolder = "C:\\Backups";
+ bool replaceBinaries = false;
+ PublishInfo publishInfo = null;
+
+ String serialNumber = _machineProvider.Machine.SerialNumber;
+
+ //Create temporary folders for packages.
+ var _newPackageTempFolder = TemporaryManager.CreateFolder();
+ _newPackageTempFolder.Persist = true;
+
+ try
{
- LogManager.Log($"Starting machine update from update package '{fileName}'...");
+ _isUpdating = true;
- //Create temporary folders for packages.
- var _newPackageTempFolder = TemporaryManager.CreateFolder();
- _newPackageTempFolder.Persist = true;
+ LogManager.Log($"Starting machine update (TUP) for serial number {serialNumber}...");
- LogManager.Log("Extracting downloaded zip file...");
- //Extract software package.
- ZipFile.ExtractToDirectory(fileName, _newPackageTempFolder);
+ //Connecting to machine...
+ LogManager.Log("Verifying machine connection and state...");
+
+ UpdateProgress("Verifying machine state", "Initializing...");
+
+ await Task.Delay(1000);
+
+ IMachineOperator op = _machineProvider.MachineOperator;
+
+ if (setupFirmware)
+ {
+ LogManager.Log("Machine is configured to update firmware...");
+
+ if (op.State != Transport.TransportComponentState.Connected)
+ {
+ throw LogManager.Log(new InvalidOperationException("Could not perform an update while the machine is not connected."));
+ }
+ }
+
+ if (!op.CanPrint)
+ {
+ throw LogManager.Log(new InvalidOperationException($"Could not perform an update while the machine is in {op.Status} status."));
+ }
+
+ UpdateProgress("Exploring package", "Extracting...");
+ LogManager.Log("Extracting package...");
+
+ LogManager.Log($"Temporary package folder created: {_newPackageTempFolder}.");
+
+ await Task.Factory.StartNew(() =>
+ {
+ //Extract software package.
+ ZipFile.ExtractToDirectory(fileName, _newPackageTempFolder);
+ });
+
+ //Extracting publish info
+ UpdateProgress("Exploring package", "Verifying...");
+ publishInfo = PublishInfo.FromJson(File.ReadAllText(Path.Combine(_newPackageTempFolder, "version.json")));
+
+ if (!publishInfo.IsMachineTupPackage)
+ {
+ throw new InvalidOperationException("The specified tup file is invalid. Updating a machine from a tup file requires a custom generated package.");
+ }
+
+ if (publishInfo.MachineSerialNumber != serialNumber)
+ {
+ throw new InvalidOperationException("The specified tup file is invalid. The package was generated for a different machine.");
+ }
+
+ if (publishInfo.MachineDeploymentSlot != _settings.DeploymentSlot)
+ {
+ throw new InvalidOperationException("The specified tup file is invalid. The package was generated on a different environment.");
+ }
+
+ replaceBinaries = _app_manager.Version.ToString() != publishInfo.ApplicationVersion;
LogManager.Log("Copying latest updater utility to application path...");
+
//Copy new updater utility to app path.
File.Copy(Path.Combine(_newPackageTempFolder, "Tango.PPC.Updater.exe"), Path.Combine(PathHelper.GetStartupPath(), "Tango.PPC.Updater.exe"), true);
- LogManager.Log("Update operation completed!");
+ //Run pre-update packages.
+ try
+ {
+ UpdateProgress("Preparing", "Running update packages...");
+ LogManager.Log("Running pre-update packages...");
+ var packagesFolder = Path.Combine(_newPackageTempFolder, "Packages");
+
+ Version updateVersion = new Version(1, 0, 0, 0);
+ try
+ {
+ updateVersion = Version.Parse(publishInfo.ApplicationVersion);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error parsing new version string for package runner.");
+ }
- return new MachineUpdateResult()
+ await _packageRunner.Run(PackageType.Pre, updateVersion, packagesFolder);
+ }
+ catch (Exception ex)
{
- UpdatePackagePath = _newPackageTempFolder,
+ LogManager.Log(ex, "Error running pre-update packages...");
+ }
+
+ //Synchronize database
+ UpdateProgress("Updating Database", "Initializing...");
+
+ UpdateProgress("Updating Database", "Connecting to local database...");
+ LogManager.Log("Initializing database manager...");
+ DbManager db = DbManager.FromDataSource(localDataSource);
+
+ LogManager.Log("Checking Tango database exists on the local machine...");
+ if (!db.Exists(localDataSource.Catalog))
+ {
+ throw new InvalidProgramException("Database tango does not exists.");
+ }
+
+ UpdateProgress("Updating Database", "Creating database backup...");
+
+ //Create Database Backup
+ try
+ {
+ Directory.CreateDirectory(backupsFolder);
+ dbBackupFile = $"{backupsFolder}\\{Path.GetRandomFileName()}.bak";
+ LogManager.Log($"Creating database backup to '{dbBackupFile}'...");
+ await Task.Factory.StartNew(() => db.Backup(localDataSource.Catalog, dbBackupFile));
+ performDatabaseRollback = true;
+ LogManager.Log("Database backup created successfully.");
+ }
+ catch (Exception ex)
+ {
+ throw LogManager.Log(ex, "Update manager error while trying to create a database backup.");
+ }
+
+ LogManager.Log("Extracting database file from package...");
+ File.Copy(Path.Combine(_newPackageTempFolder, tempDbFileName), Path.Combine(backupsFolder, tempDbFileName));
+
+ LogManager.Log("Restoring package database as a new database...");
+ db.RestoreAsNew(tempDbName, Path.Combine(backupsFolder, tempDbFileName), backupsFolder);
+
+ Core.DataSource tempDbDataSource = new Core.DataSource();
+ tempDbDataSource.Address = localDataSource.Address;
+ tempDbDataSource.IntegratedSecurity = localDataSource.IntegratedSecurity;
+ tempDbDataSource.Type = localDataSource.Type;
+ tempDbDataSource.Catalog = tempDbName;
+
+ LogManager.Log("Disposing database manager.");
+ db.Dispose();
+
+ LogManager.Log($"Initializing {nameof(ExaminerSequenceConfigurationRunner)}...");
+
+ UpdateProgress("Updating Database", "Initializing update sequence...");
+
+ ExaminerSequenceConfigurationRunner runner = new ExaminerSequenceConfigurationRunner(
+ Path.Combine(_newPackageTempFolder, "Update Scripts", "config.xml"),
+ Path.Combine(_newPackageTempFolder, "Update Scripts"),
+ tempDbDataSource,
+ localDataSource,
+ _machineProvider.Machine.Guid);
+
+ runner.Log += (x, msg) =>
+ {
+ LogManager.Log(msg);
+ ProgressLog?.Invoke(this, msg);
};
- });
+
+ runner.ScriptExecuting += (x, item) =>
+ {
+ LogManager.Log($"Executing script {item.ToString()}...");
+ UpdateProgress("Updating Database", item.Name + "...");
+ };
+
+ LogManager.Log("Starting synchronization process...");
+
+ try
+ {
+ await runner.Run();
+ LogManager.Log("Synchronization completed successfully!");
+ UpdateProgress("Updating Database", "Database synchronization completed successfully.");
+ }
+ catch (Exception ex)
+ {
+ throw LogManager.Log(ex, "Update manager error while trying to synchronize database.");
+ }
+
+ LogManager.Log("Getting setup firmware/fpga directly from db..");
+
+ using (var dbManager = DbManager.FromDataSource(localDataSource))
+ {
+ try
+ {
+ String firmware = dbManager.GetValue($"SELECT TOP 1 * FROM MACHINES WHERE SERIAL_NUMBER = '{serialNumber}'", "SETUP_FIRMWARE");
+ String fpga = dbManager.GetValue($"SELECT TOP 1 * FROM MACHINES WHERE SERIAL_NUMBER = '{serialNumber}'", "SETUP_FPGA");
+
+ setupFirmware = bool.Parse(firmware);
+ setupFPGA = bool.Parse(fpga);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error getting new values of SETUP_FIRMWARE and SETUP_FPGA.");
+ }
+ }
+
+ //Updating firmware
+ if (setupFirmware)
+ {
+ UpdateProgress("Updating Firmware", "Connecting to firmware device...");
+ LogManager.Log("");
+ LogManager.Log("-------------------------------------------------------------------------");
+ LogManager.Log("Updating Firmware...");
+
+ UpdateProgress("Updating Firmware", "Loading firmware package...");
+ var tfpPath = Path.Combine(_newPackageTempFolder, "firmware_package.tfp");
+ var stream = new FileStream(tfpPath, FileMode.Open);
+
+ if (setupFPGA)
+ {
+ op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
+ }
+ else
+ {
+ op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU;
+ }
+
+ var handler = await op.UpgradeFirmware(stream, _machineProvider.Machine.IsDemo);
+ handler.Failed += (_, ex) =>
+ {
+ stream.Dispose();
+ OnFailed(ex, result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder, publishInfo);
+ };
+ handler.Completed += (_, __) =>
+ {
+ UpdateProgress("Updating Firmware", "Firmware update completed successfully.");
+ stream.Dispose();
+ OnCompleted(new MachineUpdateResult()
+ {
+ UpdatePackagePath = _newPackageTempFolder,
+ RequiresBinariesUpdate = replaceBinaries,
+ }, result, null, tempDbName, backupsFolder, localDataSource, publishInfo);
+ };
+ handler.Canceled += (_, __) =>
+ {
+ stream.Dispose();
+ OnFailed(new Exception("The operation has been canceled."), result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder, publishInfo);
+ };
+ handler.Progress += (_, e) =>
+ {
+ UpdateProgress("Updating Firmware", e.Message, e.IsIndeterminate, e.Current, e.Total);
+ };
+ }
+ else
+ {
+ OnCompleted(new MachineUpdateResult()
+ {
+ UpdatePackagePath = _newPackageTempFolder,
+ RequiresBinariesUpdate = replaceBinaries,
+ }, result, null, tempDbName, backupsFolder, localDataSource, publishInfo);
+ }
+ }
+ catch (Exception ex)
+ {
+ OnFailed(ex, result, null, performDatabaseRollback, dbBackupFile, backupsFolder, tempDbName, localDataSource, _newPackageTempFolder, publishInfo);
+ }
+
+ return await result.Task;
+ }
+
+ /// <summary>
+ /// Performs a firmware upgrade from the specified TFP file.
+ /// </summary>
+ /// <param name="fileName">Name of the file.</param>
+ /// <returns></returns>
+ /// <exception cref="InvalidOperationException">
+ /// Could not perform a firmware upgrade while the machine is not connected.
+ /// or
+ /// </exception>
+ public async Task UpdateFromTFP(String fileName)
+ {
+ _updateStartDate = DateTime.UtcNow;
+ _logs.Clear();
+
+ TaskCompletionSource<object> result = new TaskCompletionSource<object>();
+
+ String version = String.Empty;
+ Stream stream = null;
+
+ try
+ {
+ _isUpdating = true;
+
+ IMachineOperator op = _machineProvider.MachineOperator;
+
+ UpdateProgress("Updating Firmware", "Loading firmware package...");
+ stream = new FileStream(fileName, FileMode.Open);
+
+ var packageInfo = await op.GetFirmwarePackageInfo(stream);
+ stream.Position = 0;
+ version = packageInfo.FileDescriptors.FirstOrDefault(x => x.Destination == PMR.FirmwareUpgrade.VersionFileDestination.Mcu)?.Version;
+
+ LogManager.Log("Verifying machine connection and state...");
+
+ UpdateProgress("Verifying machine state", "Initializing...");
+
+ await Task.Delay(1000);
+
+ if (op.State != Transport.TransportComponentState.Connected)
+ {
+ throw LogManager.Log(new InvalidOperationException("Could not perform a firmware upgrade while the machine is not connected."));
+ }
+ if (!op.CanPrint)
+ {
+ throw LogManager.Log(new InvalidOperationException($"Could not perform a firmware upgrade while the machine is in {op.Status} status."));
+ }
+
+ UpdateProgress("Updating Firmware", "Connecting to firmware device...");
+ LogManager.Log("");
+ LogManager.Log("-------------------------------------------------------------------------");
+ LogManager.Log("Updating Firmware...");
+
+ op.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
+
+ var handler = await op.UpgradeFirmware(stream, _machineProvider.Machine.IsDemo);
+ handler.Failed += (_, ex) =>
+ {
+ stream.Dispose();
+ OnFailed(ex, result, version);
+ };
+ handler.Completed += (_, __) =>
+ {
+ UpdateProgress("Updating Firmware", "Firmware update completed successfully.");
+ stream.Dispose();
+ OnCompleted(result, version);
+ };
+ handler.Canceled += (_, __) =>
+ {
+ stream.Dispose();
+ OnFailed(new Exception("The operation has been canceled."), result, version);
+ };
+ handler.Progress += (_, e) =>
+ {
+ UpdateProgress("Updating Firmware", e.Message, e.IsIndeterminate, e.Current, e.Total);
+ };
+ }
+ catch (Exception ex)
+ {
+ try
+ {
+ if (stream != null)
+ {
+ stream.Dispose();
+ }
+ }
+ catch { }
+
+ OnFailed(ex, result, version);
+ }
+
+ await result.Task;
}
/// <summary>
@@ -631,25 +1596,88 @@ namespace Tango.PPC.Common.MachineUpdate
/// </summary>
/// <param name="filePath">The file path.</param>
/// <returns></returns>
- public Task<UpdatePackageFile> GetUpdatePackageFileInfo(string filePath)
+ public Task<PublishInfo> GetUpdatePackageFileInfo(string filePath)
{
- return Task.Factory.StartNew<UpdatePackageFile>(() =>
+ return Task.Factory.StartNew<PublishInfo>(() =>
{
- UpdatePackageFile file = new UpdatePackageFile();
- var tempFolder = TemporaryManager.CreateFolder();
-
using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile(filePath))
{
- var appEntry = zip.Entries.SingleOrDefault(x => x.FileName == "Tango.PPC.UI.exe");
- appEntry.Extract(tempFolder);
+ var appEntry = zip.Entries.SingleOrDefault(x => x.FileName == "version.json");
+ var reader = appEntry.OpenReader();
+
+ using (StreamReader stReader = new StreamReader(reader))
+ {
+ String json = stReader.ReadToEnd();
+ reader.Dispose();
+
+ return PublishInfo.FromJson(json);
+ }
}
+ });
+ }
+
+ /// <summary>
+ /// Checks whether any post update packages needs to be installed.
+ /// </summary>
+ /// <returns></returns>
+ public Task<bool> PostUpdatePackagesRequired()
+ {
+ String packagesFolder = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "packages");
+ return _packageRunner.IsPackageInstallationRequired(PackageType.Post, packagesFolder);
+ }
+
+ /// <summary>
+ /// Runs all post update packages.
+ /// </summary>
+ /// <returns></returns>
+ public Task<PackageRunnerResult> RunPostUpdatePackages()
+ {
+ String packagesFolder = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "packages");
- FileVersionInfo info = FileVersionInfo.GetVersionInfo(Path.Combine(tempFolder, "Tango.PPC.UI.exe"));
- file.Version = Version.Parse(info.ProductVersion);
+ Version previousVersion = null;
+ String str = _settings.PreviousApplicationVersion;
- tempFolder.Delete();
+ if (Version.TryParse(str, out previousVersion))
+ {
+ return _packageRunner.Run(PackageType.Post, previousVersion, packagesFolder);
+ }
+ else
+ {
+ throw new InvalidCastException($"Error parsing the previous version string '{str}'.");
+ }
+ }
- return file;
+ public Task RestoreLastDatabaseBackup()
+ {
+ return Task.Factory.StartNew(() =>
+ {
+ LogManager.Log("Rolling back database changes...");
+ UpdateProgress("Rollback", "Rolling back database changes...");
+
+ var localDataSource = SettingsManager.Default.GetOrCreate<CoreSettings>().DataSource;
+ var lastBackupFile = SettingsManager.Default.GetOrCreate<PPCSettings>().LastDatabaseBackupFile;
+
+ using (DbManager db = DbManager.FromDataSource(localDataSource))
+ {
+ try
+ {
+ db.Restore(localDataSource.Catalog, lastBackupFile);
+ LogManager.Log("Database restored successfully.");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Could not rollback the database after a failed updater.");
+ throw ex;
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(lastBackupFile);
+ }
+ catch { }
+ }
+ }
});
}
@@ -677,5 +1705,56 @@ namespace Tango.PPC.Common.MachineUpdate
}
#endregion
+
+ #region Auto Check For Update
+
+ private async void _checkForUpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ if (EnableAutoCheckForUpdates && _settings.AutoCheckForUpdates && !_isUpdating)
+ {
+ _checkForUpdateTimer.Stop();
+
+ try
+ {
+ var response = await CheckForUpdate();
+ if (response.IsUpdateAvailable || response.IsDatabaseUpdateAvailable)
+ {
+ LogManager.Log($"New {(response.IsDatabaseUpdateAvailable ? "database updates" : "application version")} detected ({response.Version}). Raising event...");
+ UpdateAvailable?.Invoke(this, response);
+ }
+ }
+ catch { }
+
+ _checkForUpdateTimer.Start();
+ }
+ }
+
+ #endregion
+
+ #region External Bridge
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ //Do nothing.
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(GetUpdatesAndPackagesRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnGetUpdatesAndPackagesRequest(GetUpdatesAndPackagesRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ var updates = await db.TangoUpdates.OrderByDescending(x => x.StartDate).ToListAsync();
+ var updatesDTO = updates.Select(x => TangoUpdateDTO.FromObservable(x)).ToList();
+ var packages = (await _packageRunner.GetPackagesFile()).PackageInstallations;
+
+ var response = new GetUpdatesAndPackagesResponse();
+ response.Updates.AddRange(updatesDTO);
+ response.Packages.AddRange(packages);
+
+ await receiver.SendGenericResponse(response, token);
+ }
+ }
+
+ #endregion
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs
index 17ae394ee..85dd5b7d2 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateResult.cs
@@ -12,5 +12,18 @@ namespace Tango.PPC.Common.MachineUpdate
/// Gets or sets the temporary update package path from which to get the last downloaded software version.
/// </summary>
public String UpdatePackagePath { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the application should replace it's binaries.
+ /// </summary>
+ public bool RequiresBinariesUpdate { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MachineUpdateResult"/> class.
+ /// </summary>
+ public MachineUpdateResult()
+ {
+ RequiresBinariesUpdate = true;
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Models/FineTuneItem.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Models/FineTuneItem.cs
index 2eea5c3ce..c03be1ae9 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Models/FineTuneItem.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Models/FineTuneItem.cs
@@ -18,6 +18,8 @@ namespace Tango.PPC.Common.Models
{
public event Action SelectedChanged;
+ public BrushStop BrushStop { get; set; }
+
/// <summary>
/// Gets or sets the brush stops.
/// </summary>
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationManager.cs
index 8df0a7fb8..1fcdb4410 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationManager.cs
@@ -13,6 +13,11 @@ namespace Tango.PPC.Common.Navigation
public interface INavigationManager
{
/// <summary>
+ /// Occurs when the current view model has changed.
+ /// </summary>
+ event EventHandler<PPCViewModel> CurrentVMChanged;
+
+ /// <summary>
/// Gets the current module.
/// </summary>
IPPCModule CurrentModule { get; }
@@ -28,6 +33,16 @@ namespace Tango.PPC.Common.Navigation
bool CanNavigateBack { get; }
/// <summary>
+ /// Gets a value indicating whether the back should be enabled.
+ /// </summary>
+ bool IsBackEnabled { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the navigation system is currently navigating.
+ /// </summary>
+ bool IsNavigating { get; set; }
+
+ /// <summary>
/// Navigates to the previous view if <see cref="CanNavigateBack"/> is true.
/// </summary>
Task<bool> NavigateBack();
@@ -79,7 +94,7 @@ namespace Tango.PPC.Common.Navigation
/// Navigates to the specified module and view by full path (e.g Jobs.JobsView).
/// </summary>
/// <param name="fullPath">The full path.</param>
- Task<bool> NavigateTo(String fullPath, bool pushToHistory = true);
+ Task<bool> NavigateTo(String fullPath, bool pushToHistory = true, Action<PPCViewModel, PPCViewModel> onNavigating = null, Action<PPCViewModel, PPCViewModel> onNavigated = null);
/// <summary>
/// Navigates to the specified module and view with the specified object and expecting a return parameter.
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/NavigationView.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/NavigationView.cs
index b4562054c..643908e87 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/NavigationView.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/NavigationView.cs
@@ -22,6 +22,7 @@ namespace Tango.PPC.Common.Navigation
HomeModule,
ShutdownView,
RestartingSystemView,
- EmergencyView
+ EmergencyView,
+ RestartingView,
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarItem.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarItem.cs
index 1c47d2a97..fdd66a56b 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarItem.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarItem.cs
@@ -13,6 +13,16 @@ namespace Tango.PPC.Common.Notifications
/// </summary>
public abstract class AppBarItem : ItemBase
{
+ private AppBarPriority _priority;
+ public AppBarPriority Priority
+ {
+ get { return _priority; }
+ set { _priority = value; RaisePropertyChangedAuto(); }
+ }
+ public AppBarItem()
+ {
+ Priority = AppBarPriority.Normal;
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarPriority.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarPriority.cs
new file mode 100644
index 000000000..bd8547f5d
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/AppBarPriority.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.Notifications
+{
+ public enum AppBarPriority
+ {
+ Low,
+ Normal,
+ High
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/INotificationProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/INotificationProvider.cs
index c4e82b7d2..950b8d23f 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/INotificationProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/INotificationProvider.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -23,19 +24,24 @@ namespace Tango.PPC.Common.Notifications
ObservableCollection<NotificationItem> NotificationItems { get; }
/// <summary>
+ /// Gets the notification items view.
+ /// </summary>
+ ICollectionView NotificationItemsView { get; }
+
+ /// <summary>
/// Gets the collection of taskbar items.
/// </summary>
ObservableCollection<TaskBarItem> TaskBarItems { get; }
/// <summary>
- /// Gets the current application bar item.
+ /// Gets the application bar items.
/// </summary>
- AppBarItem CurrentAppBarItem { get; }
+ ObservableCollection<AppBarItem> AppBarItems { get; }
/// <summary>
- /// Gets a value indicating whether this instance has application bar item.
+ /// Gets a value indicating whether this instance has any application bar items.
/// </summary>
- bool HasAppBarItem { get; }
+ bool HasAppBarItems { get; }
/// <summary>
/// Gets a value indicating whether this instance has notification items.
@@ -176,7 +182,7 @@ namespace Tango.PPC.Common.Notifications
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
- AppBarItem PushAppBarItem<T>() where T : AppBarItem;
+ T PushAppBarItem<T>() where T : AppBarItem;
/// <summary>
/// Pops the application bar item.
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItem.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItem.cs
index c96fe9dee..6a29511a9 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItem.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItem.cs
@@ -14,22 +14,22 @@ namespace Tango.PPC.Common.Notifications
/// </summary>
public abstract class NotificationItem : ItemBase
{
- /// <summary>
- /// Initializes a new instance of the <see cref="NotificationItem"/> class.
- /// </summary>
- public NotificationItem() : base()
+ public enum NotificationPriority
{
- CanClose = true;
+ Low,
+ Normal,
+ High,
+ VeryHigh,
+ Critical,
}
- private bool _isExpanded;
/// <summary>
- /// Gets or sets a value indicating whether the notification panel is expanded.
+ /// Initializes a new instance of the <see cref="NotificationItem"/> class.
/// </summary>
- public bool IsExpanded
+ public NotificationItem() : base()
{
- get { return _isExpanded; }
- set { _isExpanded = value; RaisePropertyChangedAuto(); }
+ CanClose = true;
+ Priority = NotificationPriority.Normal;
}
private bool _canClose;
@@ -43,6 +43,11 @@ namespace Tango.PPC.Common.Notifications
}
/// <summary>
+ /// Gets or sets the notification priority.
+ /// </summary>
+ public NotificationPriority Priority { get; set; }
+
+ /// <summary>
/// Called when the item has been pressed.
/// </summary>
protected override void OnPreesed()
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItem.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItem.cs
index a9de336a1..7d85ef6a7 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItem.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItem.cs
@@ -77,11 +77,12 @@ namespace Tango.PPC.Common.Notifications.NotificationItems
/// <param name="expandedMessage">The expanded message.</param>
/// <param name="type">The type.</param>
/// <param name="pressedAction">The pressed action.</param>
- public MessageNotificationItem(String message, String expandedMessage, MessageNotificationItemTypes type, Action pressedAction) : this()
+ public MessageNotificationItem(String message, String expandedMessage, MessageNotificationItemTypes type, Action pressedAction, NotificationPriority priority = NotificationPriority.Normal) : this()
{
Message = message;
ExpandedMessage = expandedMessage;
MessageType = type;
+ Priority = priority;
Pressed += (_, __) => pressedAction?.Invoke();
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItemView.xaml b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItemView.xaml
index 33c58f51e..cab40e50e 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItemView.xaml
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Notifications/NotificationItems/MessageNotificationItemView.xaml
@@ -111,20 +111,20 @@
</touch:TouchIcon.Style>
</touch:TouchIcon>
- <StackPanel Margin="10 0 0 0" VerticalAlignment="Center">
- <TextBlock Text="{Binding Message}" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" Foreground="Black" VerticalAlignment="Center"></TextBlock>
+ <StackPanel Margin="10 5 40 5" VerticalAlignment="Center">
+ <TextBlock Text="{Binding Message}" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" Foreground="Black" VerticalAlignment="Center" ></TextBlock>
- <Canvas Margin="0 5 0 0">
- <TextBlock Foreground="{StaticResource TangoDarkForegroundBrush}" Text="{Binding ExpandedMessage}" TextWrapping="Wrap" VerticalAlignment="Center">
- <TextBlock.Opacity>
+
+ <TextBlock Margin="0 5 0 0" Foreground="{StaticResource TangoDarkForegroundBrush}" Text="{Binding ExpandedMessage}" FontSize="{StaticResource TangoSmallFontSize}" TextWrapping="Wrap" VerticalAlignment="Center" >
+ <!--<TextBlock.Opacity>
<MultiBinding Converter="{StaticResource heightToOpacityConverter}">
<Binding Path="ActualHeight" ElementName="MessageNotificationItemControl" />
<Binding Path="MinHeight" ElementName="MessageNotificationItemControl" />
<Binding Path="MaxHeight" ElementName="MessageNotificationItemControl" />
</MultiBinding>
- </TextBlock.Opacity>
+ </TextBlock.Opacity>-->
</TextBlock>
- </Canvas>
+
</StackPanel>
</DockPanel>
</ContentControl>
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/OS/DefaultOperationSystemManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/OS/DefaultOperationSystemManager.cs
index 2164a71c3..32fd74646 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/OS/DefaultOperationSystemManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/OS/DefaultOperationSystemManager.cs
@@ -232,5 +232,27 @@ namespace Tango.PPC.Common.OS
return Environment.MachineName;
});
}
+
+ /// <summary>
+ /// Sets the device host name.
+ /// </summary>
+ /// <returns></returns>
+ public async Task SetDeviceName(String name)
+ {
+ var command = new CmdCommand("wmic", $"computersystem where caption='{Environment.MachineName}' rename '{name}'");
+ await command.Run();
+ }
+
+ /// <summary>
+ /// Opens the operating system shell (explorer).
+ /// </summary>
+ public void OpenShell()
+ {
+ Process.Start(new ProcessStartInfo()
+ {
+ FileName = @"C:\Windows\Sysnative\cmd.exe",
+ Arguments = @"/c start /B explorer.exe"
+ });
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/OS/IOperationSystemManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/OS/IOperationSystemManager.cs
index 3e24ffe72..4faef33f9 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/OS/IOperationSystemManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/OS/IOperationSystemManager.cs
@@ -57,6 +57,12 @@ namespace Tango.PPC.Common.OS
Task<String> GetDeviceName();
/// <summary>
+ /// Sets the device host name.
+ /// </summary>
+ /// <returns></returns>
+ Task SetDeviceName(String name);
+
+ /// <summary>
/// Restarts the system.
/// </summary>
/// <returns></returns>
@@ -67,5 +73,10 @@ namespace Tango.PPC.Common.OS
/// </summary>
/// <returns></returns>
void Shutdown();
+
+ /// <summary>
+ /// Opens the operating system shell (explorer).
+ /// </summary>
+ void OpenShell();
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs
index b1bc3faad..aacbe8901 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs
@@ -4,9 +4,13 @@ using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
+using Tango.BL.Enumerations;
+using Tango.Integration.Operation;
using Tango.Logging;
+using Tango.PMR.Integration;
using Tango.PMR.Printing;
using Tango.Settings;
+using Tango.Transport.Adapters;
using Tango.Web;
namespace Tango.PPC.Common
@@ -18,11 +22,6 @@ namespace Tango.PPC.Common
public class PPCSettings : SettingsBase
{
/// <summary>
- /// Gets or sets the logging categories.
- /// </summary>
- public List<LogCategory> LoggingCategories { get; set; }
-
- /// <summary>
/// Gets or sets the state of the application.
/// </summary>
public ApplicationStates ApplicationState { get; set; }
@@ -138,6 +137,157 @@ namespace Tango.PPC.Common
public bool EnableJobLiquidQuantityValidation { get; set; }
/// <summary>
+ /// Gets or sets the job number of units method.
+ /// </summary>
+ public JobUnitsMethods JobUnitsMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets the loaded RML unique identifier.
+ /// </summary>
+ public String LoadedRmlGuid { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default RML unique identifier.
+ /// </summary>
+ public String DefaultRmlGuid { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default color space unique identifier.
+ /// </summary>
+ public List<ColorSpaces> SupportedColorSpaces { get; set; }
+
+ /// <summary>
+ /// Gets or sets the target job types.
+ /// </summary>
+ public List<JobTypes> SupportedJobTypes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default spool type unique identifier.
+ /// </summary>
+ public String DefaultSpoolTypeGuid { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default length of the segment.
+ /// </summary>
+ public int DefaultSegmentLength { get; set; }
+
+ /// <summary>
+ /// Gets or sets the previous application version.
+ /// </summary>
+ public String PreviousApplicationVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether synchronize jobs with twine server.
+ /// </summary>
+ public bool SynchronizeJobs { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether synchronize diagnostics data.
+ /// </summary>
+ public bool SynchronizeDiagnostics { get; set; }
+
+ /// <summary>
+ /// Gets or sets the synchronization interval.
+ /// </summary>
+ public TimeSpan SynchronizationInterval { get; set; }
+
+ /// <summary>
+ /// Gets or sets the known firmware version.
+ /// </summary>
+ public String FirmwareVersion { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to display the power up screen.
+ /// </summary>
+ public bool DisplayPowerUpScreen { get; set; }
+
+ /// <summary>
+ /// Gets or sets the power up screen timeout.
+ /// </summary>
+ public TimeSpan PowerUpScreenTimeout { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to automatically check for software and database (quick) updates.
+ /// </summary>
+ public bool AutoCheckForUpdates { get; set; }
+
+ /// <summary>
+ /// Gets or sets the automatic update check interval.
+ /// </summary>
+ public TimeSpan AutoUpdateCheckInterval { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable the automatic thread loading support.
+ /// </summary>
+ public bool EnableAutomaticThreadLoading { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to display the thread loading screen.
+ /// </summary>
+ public bool DisplayAutomaticThreadLoadingScreen { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable embedded debug logs.
+ /// </summary>
+ public bool EnableEmbeddedDebugLogs { get; set; }
+
+ /// <summary>
+ /// Gets or sets the TCP transport adapter write mode.
+ /// </summary>
+ public TcpTransportAdapterWriteMode TcpTransportAdapterWriteMode { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to expose the external bridge service via SignalR.
+ /// </summary>
+ public bool EnableExternalBridgeSignalR { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name of the exteral bridge SignalR hub.
+ /// </summary>
+ public String ExternalBridgeSignalRHub { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable the internal remote desktop service.
+ /// </summary>
+ public bool EnableRemoteDesktop { get; set; }
+
+ /// <summary>
+ /// Gets or sets the internal remote desktop service frame rate (1-20).
+ /// </summary>
+ public int RemoteDesktopFrameRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable insights.
+ /// </summary>
+ public bool InsightsEnabled { get; set; }
+
+ /// <summary>
+ /// Gets or sets the insights sampling interval.
+ /// </summary>
+ public TimeSpan InsightsSamplingInterval { get; set; }
+
+ /// <summary>
+ /// Gets or sets the insights storage cleanup interval.
+ /// </summary>
+ public TimeSpan InsightsStorageCleanupInterval { get; set; }
+
+ /// <summary>
+ /// Gets or sets the duration of the insights maximum storage duration.
+ /// </summary>
+ public TimeSpan InsightsMaxStorageDuration { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to bypass Internet connectivity checks before attempting to perform an update for example.
+ /// </summary>
+ public bool BypassInternetConnectivityCheck { get; set; }
+
+ /// <summary>
+ /// Gets or sets the last database backup file that was generated before application update.
+ /// If updater utility was successful, this file should be deleted. Otherwise should be restored.
+ /// </summary>
+ public String LastDatabaseBackupFile { get; set; }
+
+ /// <summary>
/// Gets the machine service address.
/// </summary>
/// <returns></returns>
@@ -155,18 +305,44 @@ namespace Tango.PPC.Common
EnableGradientGeneration = true;
GradientGenerationResolution = 20;
MachineScanningTimeoutSeconds = 20;
- LoggingCategories = new List<LogCategory>();
EmbeddedComPort = "COM10";
EmbeddedDeviceHint = "Tango USB Serial Port";
ExternalBridgePassword = "Aa123456";
HotSpotPassword = "Aa123456";
LockScreenTimeout = TimeSpan.FromMinutes(10);
LockScreenPassword = "1111";
- DeploymentSlot = DeploymentSlot.TEST;
+ DeploymentSlot = DeploymentSlot.DEV;
EnableWatchDog = true;
EnableEmergencyNotifications = true;
EmergencyComPort = "COM2";
EnableJobLiquidQuantityValidation = true;
+ JobUnitsMethod = JobUnitsMethods.Device;
+ DefaultSegmentLength = 100;
+ SupportedColorSpaces = new List<ColorSpaces>();
+ SupportedJobTypes = new List<JobTypes>();
+ PreviousApplicationVersion = "1.0.0.0";
+ SynchronizeJobs = false;
+ SynchronizeDiagnostics = true;
+ SynchronizationInterval = TimeSpan.FromMinutes(60);
+ FirmwareVersion = "1.0.0.0";
+ DisplayPowerUpScreen = true;
+ PowerUpScreenTimeout = TimeSpan.FromSeconds(60);
+ AutoCheckForUpdates = true;
+ AutoUpdateCheckInterval = TimeSpan.FromMinutes(30);
+ EnableAutomaticThreadLoading = true;
+ DisplayAutomaticThreadLoadingScreen = true;
+ EnableEmbeddedDebugLogs = true;
+ TcpTransportAdapterWriteMode = TcpTransportAdapterWriteMode.Interval;
+ EnableExternalBridgeSignalR = true;
+ ExternalBridgeSignalRHub = "ExternalBridgeHub";
+ EnableRemoteDesktop = true;
+ RemoteDesktopFrameRate = 5;
+ BypassInternetConnectivityCheck = false;
+
+ InsightsEnabled = true;
+ InsightsSamplingInterval = TimeSpan.FromMinutes(1);
+ InsightsMaxStorageDuration = TimeSpan.FromDays(30);
+ InsightsStorageCleanupInterval = TimeSpan.FromMinutes(60);
}
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs
index 5e584f891..98eef6883 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs
@@ -16,7 +16,10 @@ using Tango.PPC.Common.Navigation;
using Tango.PPC.Common.Notifications;
using Tango.PPC.Common.Printing;
using Tango.PPC.Common.RemoteAssistance;
+using Tango.PPC.Common.RemoteDesktop;
using Tango.PPC.Common.Storage;
+using Tango.PPC.Common.Synchronization;
+using Tango.PPC.Common.ThreadLoading;
using Tango.Settings;
using Tango.SharedUI;
using static Tango.SharedUI.Controls.NavigationControl;
@@ -27,7 +30,7 @@ namespace Tango.PPC.Common
/// Represents a PPC view model base class.
/// </summary>
/// <seealso cref="Tango.SharedUI.ViewModel" />
- public abstract class PPCViewModel : ViewModel, INavigationViewModel, INavigationBlocker
+ public abstract class PPCViewModel : ViewModel, INavigationBlocker
{
/// <summary>
/// Gets the static observable entities adapter.
@@ -109,6 +112,24 @@ namespace Tango.PPC.Common
[TangoInject]
public IEventLogger EventLogger { get; set; }
+ /// <summary>
+ /// Gets or sets the machine data synchronizer.
+ /// </summary>
+ [TangoInject]
+ public IMachineDataSynchronizer MachineDataSynchronizer { get; set; }
+
+ /// <summary>
+ /// Gets or sets the remote desktop service.
+ /// </summary>
+ [TangoInject]
+ public IRemoteDesktopService RemoteDesktopService { get; set; }
+
+ /// <summary>
+ /// Gets or sets the thread loading service.
+ /// </summary>
+ [TangoInject]
+ public IThreadLoadingService ThreadLoadingService { get; set; }
+
private PPCSettings _settings;
/// <summary>
/// Gets the main PPC settings.
@@ -159,6 +180,15 @@ namespace Tango.PPC.Common
}
/// <summary>
+ /// Called when the navigation system has navigated to this VM view.
+ /// </summary>
+ /// <param name="fromVM">The view model instance of the previous view model</param>
+ public virtual void OnNavigatedTo(PPCViewModel fromVM)
+ {
+
+ }
+
+ /// <summary>
/// Called when the navigation system has navigated from this VM view.
/// </summary>
public virtual void OnNavigatedFrom()
@@ -167,6 +197,22 @@ namespace Tango.PPC.Common
}
/// <summary>
+ /// Called before the navigation system has navigated to this VM view.
+ /// </summary>
+ public virtual void OnBeforeNavigatedTo()
+ {
+
+ }
+
+ /// <summary>
+ /// Called before the navigation system has navigated from this VM view.
+ /// </summary>
+ public virtual void OnBeforeNavigatedFrom()
+ {
+ IsVisible = false;
+ }
+
+ /// <summary>
/// Raises the specified message using the default <see cref="TangoMessenger"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
@@ -220,7 +266,7 @@ namespace Tango.PPC.Common
/// </summary>
public virtual void OnApplicationReady()
{
-
+
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/DefaultPerformanceService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/DefaultPerformanceService.cs
new file mode 100644
index 000000000..59236f667
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/DefaultPerformanceService.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Integration.ExternalBridge;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.PPC.Shared.Performance;
+
+namespace Tango.PPC.Common.Performance
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultPerformanceService : ExtendedObject, IPerformanceService
+ {
+ #region Nested Classes
+
+ public static class PerformanceInfo
+ {
+ [DllImport("psapi.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetPerformanceInfo([Out] out PerformanceInformation PerformanceInformation, [In] int Size);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct PerformanceInformation
+ {
+ public int Size;
+ public IntPtr CommitTotal;
+ public IntPtr CommitLimit;
+ public IntPtr CommitPeak;
+ public IntPtr PhysicalTotal;
+ public IntPtr PhysicalAvailable;
+ public IntPtr SystemCache;
+ public IntPtr KernelTotal;
+ public IntPtr KernelPaged;
+ public IntPtr KernelNonPaged;
+ public IntPtr PageSize;
+ public int HandlesCount;
+ public int ProcessCount;
+ public int ThreadCount;
+ }
+
+ public static Int64 GetPhysicalAvailableMemoryInMiB()
+ {
+ PerformanceInformation pi = new PerformanceInformation();
+ if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi)))
+ {
+ return Convert.ToInt64((pi.PhysicalAvailable.ToInt64() * pi.PageSize.ToInt64() / 1048576));
+ }
+ else
+ {
+ return -1;
+ }
+
+ }
+
+ public static Int64 GetTotalMemoryInMiB()
+ {
+ PerformanceInformation pi = new PerformanceInformation();
+ if (GetPerformanceInfo(out pi, Marshal.SizeOf(pi)))
+ {
+ return Convert.ToInt64((pi.PhysicalTotal.ToInt64() * pi.PageSize.ToInt64() / 1048576));
+ }
+ else
+ {
+ return -1;
+ }
+
+ }
+ }
+
+ #endregion
+
+ private class PerformanceClient
+ {
+ public ExternalBridgeReceiver Receiver { get; set; }
+ public String Token { get; set; }
+ }
+
+ private List<PerformanceClient> _clients;
+ private PerformancePackage _package;
+ private bool _isStarted;
+ private Thread _performanceThread;
+
+ public bool Enabled { get; set; } = true;
+
+ public DefaultPerformanceService(IPPCExternalBridgeService externalBridge)
+ {
+ _package = new PerformancePackage();
+ _clients = new List<PerformanceClient>();
+ externalBridge.RegisterRequestHandler(this);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(StartPerformanceUpdatesRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnStartPerformanceUpdatesRequest(StartPerformanceUpdatesRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ if (!_clients.Exists(x => x.Receiver == receiver))
+ {
+ _clients.Add(new PerformanceClient() { Receiver = receiver, Token = token });
+ OnReceiversChanged();
+ }
+
+ await receiver.SendGenericResponse(new StartPerformanceUpdatesResponse() { Package = _package }, token);
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ _clients.RemoveAll(x => x.Receiver == receiver);
+ OnReceiversChanged();
+ }
+
+ private void OnReceiversChanged()
+ {
+ if (_clients.Count > 0 && !_isStarted)
+ {
+ _isStarted = true;
+ _performanceThread = new Thread(PerformanceThreadMethod);
+ _performanceThread.IsBackground = true;
+ _performanceThread.Start();
+ }
+ else if (_clients.Count == 0 && _isStarted)
+ {
+ _isStarted = false;
+ }
+ }
+
+ private async void PerformanceThreadMethod()
+ {
+ while (_isStarted)
+ {
+ try
+ {
+ _package.ApplicationCPU = (int)GetAppCPU();
+ _package.CPU = (int)GetTotalCPU();
+ _package.ApplicationRAM = (int)BytesToMegaBytes(GetAppRam());
+ _package.MaxRAM = (int)BytesToMegaBytes((long)new Microsoft.VisualBasic.Devices.ComputerInfo().TotalPhysicalMemory);
+ _package.RAM = _package.MaxRAM - (int)PerformanceInfo.GetPhysicalAvailableMemoryInMiB();
+
+ DriveInfo info = new DriveInfo("C");
+ _package.DiskCapacity = (int)BytesToMegaBytes(info.TotalSize);
+ _package.AvailableDiskSpace = (int)BytesToMegaBytes(info.AvailableFreeSpace);
+ _package.DateTime = DateTime.Now;
+
+ foreach (var client in _clients.ToList())
+ {
+ try
+ {
+ await client.Receiver.SendGenericResponse(new StartPerformanceUpdatesResponse() { Package = _package }, client.Token);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error sending performance package.");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error creating performance package.");
+ }
+
+ Thread.Sleep(200);
+ }
+ }
+
+ #region Helpers
+
+ private float BytesToMegaBytes(long bytes)
+ {
+ return bytes / 1024f / 1024f;
+ }
+
+ public float GetAppCPU()
+ {
+ PerformanceCounter cpuCounter = new PerformanceCounter();
+ cpuCounter.CategoryName = "Process";
+ cpuCounter.CounterName = "% Processor Time";
+ cpuCounter.InstanceName = Process.GetCurrentProcess().ProcessName;
+
+ // will always start at 0
+ float firstValue = cpuCounter.NextValue();
+ System.Threading.Thread.Sleep(1000);
+ // now matches task manager reading
+ float secondValue = cpuCounter.NextValue();
+
+ return secondValue / Environment.ProcessorCount;
+ }
+
+ public float GetTotalCPU()
+ {
+ PerformanceCounter cpuCounter = new PerformanceCounter();
+ cpuCounter.CategoryName = "Processor";
+ cpuCounter.CounterName = "% Processor Time";
+ cpuCounter.InstanceName = "_Total";
+
+ // will always start at 0
+ float firstValue = cpuCounter.NextValue();
+ System.Threading.Thread.Sleep(1000);
+ // now matches task manager reading
+ float secondValue = cpuCounter.NextValue();
+
+ return secondValue;
+ }
+
+ public long GetAppRam()
+ {
+ Process proc = Process.GetCurrentProcess();
+ return proc.PrivateMemorySize64;
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/IPerformanceService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/IPerformanceService.cs
new file mode 100644
index 000000000..29e69aee2
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Performance/IPerformanceService.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Integration.ExternalBridge;
+
+namespace Tango.PPC.Common.Performance
+{
+ public interface IPerformanceService : IPPCService, IExternalBridgeRequestHandler
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.Designer.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.Designer.cs
index 60e2bdb01..30828af87 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.Designer.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.Designer.cs
@@ -19,7 +19,7 @@ namespace Tango.PPC.Common.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -59,5 +59,25 @@ namespace Tango.PPC.Common.Properties {
resourceCulture = value;
}
}
+
+ /// <summary>
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ /// </summary>
+ internal static System.Drawing.Bitmap finger3 {
+ get {
+ object obj = ResourceManager.GetObject("finger3", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ /// </summary>
+ internal static System.Drawing.Bitmap tap {
+ get {
+ object obj = ResourceManager.GetObject("tap", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.resx b/Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.resx
index af7dbebba..ca6197f54 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.resx
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Properties/Resources.resx
@@ -46,7 +46,7 @@
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
- : System.Serialization.Formatters.Binary.BinaryFormatter
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
@@ -60,6 +60,7 @@
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
@@ -68,9 +69,10 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" />
+ <xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
@@ -85,9 +87,10 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
@@ -109,9 +112,16 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <data name="finger3" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>..\Resources\finger3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+ </data>
+ <data name="tap" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>..\Resources\tap.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+ </data>
</root> \ No newline at end of file
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PPCPublisher.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PPCPublisher.cs
index 526d4465a..1a289ff50 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PPCPublisher.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PPCPublisher.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Tango.AdvancedInstaller;
using Tango.Core;
using Tango.Core.Helpers;
+using Tango.Git;
using Tango.PMR.FirmwareUpgrade;
using Tango.PPC.Common.Web;
using Tango.SQLExaminer;
@@ -59,14 +60,11 @@ namespace Tango.PPC.Common.Publish
/// Gets the latest version.
/// </summary>
/// <returns></returns>
- public async Task<String> GetRemoteVersion(String machineVersionGuid)
+ public async Task<LatestVersionResponse> GetRemoteVersion(String machineVersionGuid)
{
_client.Environment = Options.Environment;
- var response = await _client.GetLatestVersion(new LatestVersionRequest()
- {
- MachineVersionGuid = machineVersionGuid,
- });
- return response.Version;
+ var response = await _client.GetLatestVersion(new LatestVersionRequest() { MachineVersionGuid = machineVersionGuid });
+ return response;
}
/// <summary>
@@ -163,8 +161,12 @@ namespace Tango.PPC.Common.Publish
OnPublishProgress(0, 100, $"Fetching remote version from {Options.Environment.ToAddress()}...");
- String remote_version = GetRemoteVersion(Options.MachineVersionGuid).Result;
+ var r = GetRemoteVersion(Options.MachineVersionGuid).Result;
+ String remote_version = r.Version;
+ String remote_firmware_version = r.FirmwareVersion;
+
String local_version = GetLocalVersion();
+ String local_firmware_version = GetLocalFirmwareVersion(Options.TfpPath);
OnPublishProgress(0, 100, $"Remote version: {remote_version}");
OnPublishProgress(0, 100, $"Local version: {local_version}");
@@ -174,6 +176,11 @@ namespace Tango.PPC.Common.Publish
throw new InvalidOperationException($"The local version '{local_version}' is not greater than the remote version '{remote_version}'.");
}
+ if (Version.Parse(local_firmware_version) < Version.Parse(remote_firmware_version))
+ {
+ throw new InvalidOperationException($"The local firmware version '{local_firmware_version}' is not greater than the remote version '{remote_firmware_version}'.");
+ }
+
OnPublishProgress(0, 100, $"Requesting version upload...");
var response = _client.UploadVersion(new UploadVersionRequest()
@@ -185,6 +192,7 @@ namespace Tango.PPC.Common.Publish
FirmwareVersion = GetVersionInfoFromTFP(Options.TfpPath).FileDescriptors.SingleOrDefault(x => x.Destination == VersionFileDestination.Mcu).Version,
}).Result;
+ CreateTupPackage(tempFile).Wait();
if (!String.IsNullOrWhiteSpace(Options.InstallerProject))
{
@@ -210,8 +218,6 @@ namespace Tango.PPC.Common.Publish
}
}
- CreateTupPackage(tempFile).Wait();
-
OnPublishProgress(0, 100, $"Starting version upload...");
using (StorageBlobUploader uploader = new StorageBlobUploader(response.BlobAddress, tempFile))
@@ -234,7 +240,7 @@ namespace Tango.PPC.Common.Publish
Token = response.Token,
}).Wait();
- remote_version = GetRemoteVersion(Options.MachineVersionGuid).Result;
+ remote_version = GetRemoteVersion(Options.MachineVersionGuid).Result.Version;
local_version = GetLocalVersion();
OnPublishProgress(0, 0, $"Remote version: {remote_version}");
@@ -245,6 +251,55 @@ namespace Tango.PPC.Common.Publish
throw new InvalidOperationException("The remote version does not seems to have been updated.");
}
+ if (Options.CreateTag)
+ {
+ String repoPath = Path.GetFullPath("../../../../../");
+ String tagVersion = System.Version.Parse(GetLocalVersion()).ToString(3);
+ String tagName = $"PPC_v{tagVersion}";
+
+ using (GitRepositoryManager git = new GitRepositoryManager(repoPath, Options.Email, Options.PersonalAccessToken))
+ {
+ OnPublishProgress(0, 100, "Checking repository changes...");
+ int changes = git.GetChanges().Count;
+ if (changes > 0)
+ {
+ if (Options.AutoCommitAndPush)
+ {
+ OnPublishProgress(0, 100, "Committing repository changes...");
+ git.Commit(tagName);
+ }
+ else
+ {
+ throw new InvalidOperationException($"There are {changes} uncommitted changes on the repository. Please commit and push all changes before creating the Tag");
+ }
+ }
+
+ OnPublishProgress(0, 100, "Checking outgoing commits...");
+ int commits = git.GetOutgoingCommits().Count;
+ if (commits > 0)
+ {
+ if (Options.AutoCommitAndPush)
+ {
+ OnPublishProgress(0, 100, "Pushing repository changes...");
+ git.Sync();
+ }
+ else
+ {
+ throw new InvalidOperationException($"There are {commits} outgoing commits on the repository. Please push all commits before creating the Tag");
+ }
+ }
+
+ git.Progress += (x, e) =>
+ {
+ OnPublishProgress(e.Progress.Value, e.Progress.Maximum, $"Pushing Tag '{tagName}'...");
+ };
+
+ OnPublishProgress(0, 100, $"Creating Tag '{tagName}'...");
+
+ git.CreatePushTag(tagName, Options.Comments, "Roy Ben Shabat");
+ }
+ }
+
OnPublishProgress(0, 0, "Version published successfully.");
}
catch (Exception ex)
@@ -289,13 +344,21 @@ namespace Tango.PPC.Common.Publish
using (ZipFile zip = new ZipFile())
{
- zip.AddFile(Options.TfpPath, "/").FileName = "firmware_package.tfp";
+ zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
+
+ if (Options.BuildConfig != "Debug")
+ {
+ zip.AddFile(Options.TfpPath, "/").FileName = "firmware_package.tfp";
+ }
PublishInfo versionInfo = new PublishInfo();
versionInfo.ApplicationVersion = GetLocalVersion();
versionInfo.Comments = Options.Comments;
versionInfo.Firmware = GetVersionInfoFromTFP(Options.TfpPath);
+ //Validate the package.
+ versionInfo.Firmware.Validate();
+
var versionInfoFile = TemporaryManager.CreateImaginaryFile();
File.WriteAllText(versionInfoFile, versionInfo.ToJson());
zip.AddFile(versionInfoFile, "/").FileName = "version.json";
@@ -362,6 +425,8 @@ namespace Tango.PPC.Common.Publish
var cuf = zip.AddFile(update_config_file, update_dir);
cuf.FileName = update_dir + "\\config.xml";
+ zip.AddDirectory(folder + "\\" + "Packages", "/Packages");
+
foreach (var file in Directory.GetFiles(folder, "*.*", SearchOption.TopDirectoryOnly))
{
zip.AddFile(file, "/");
@@ -404,6 +469,16 @@ namespace Tango.PPC.Common.Publish
}
/// <summary>
+ /// Gets the MCU version from the specified TFP file.
+ /// </summary>
+ /// <param name="tfpFile">The TFP file.</param>
+ /// <returns></returns>
+ public String GetLocalFirmwareVersion(String tfpFile)
+ {
+ return GetVersionInfoFromTFP(tfpFile).GetMcuVersion().ToString();
+ }
+
+ /// <summary>
/// Raises the publish progress event.
/// </summary>
/// <param name="progress">The progress.</param>
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs
index 77717254e..1bbdb80d0 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishInfo.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.PMR.FirmwareUpgrade;
+using Tango.Web;
namespace Tango.PPC.Common.Publish
{
@@ -13,6 +14,9 @@ namespace Tango.PPC.Common.Publish
public String ApplicationVersion { get; set; }
public VersionPackageDescriptor Firmware { get; set; }
public String Comments { get; set; }
+ public bool IsMachineTupPackage { get; set; }
+ public String MachineSerialNumber { get; set; }
+ public DeploymentSlot MachineDeploymentSlot { get; set; }
public PublishInfo()
{
@@ -24,9 +28,25 @@ namespace Tango.PPC.Common.Publish
return JsonConvert.SerializeObject(this);
}
- public PublishInfo FromJson(String json)
+ public static PublishInfo FromJson(String json)
{
return JsonConvert.DeserializeObject<PublishInfo>(json);
}
+
+ public String GetFirmwareVersion()
+ {
+ Version version = new Version("1.0.0.0");
+
+ var s = Firmware.FileDescriptors.FirstOrDefault(x => x.Destination == VersionFileDestination.Mcu);
+ if (s != null)
+ {
+ if (Version.TryParse(s.Version,out version))
+ {
+ return version.ToString();
+ }
+ }
+
+ return version.ToString();
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishOptions.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishOptions.cs
index 4c40acb44..399a19f0d 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishOptions.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Publish/PublishOptions.cs
@@ -17,6 +17,7 @@ namespace Tango.PPC.Common.Publish
public event EventHandler BuidConfigChanged;
public event EventHandler BasicInfoChanged;
public event EventHandler MachineVersionGuidChanged;
+ public event EventHandler TfpPathChanged;
private String basePath;
[Option("path", HelpText = "Specifies the application base path.", Required = false)]
@@ -79,7 +80,7 @@ namespace Tango.PPC.Common.Publish
public String TfpPath
{
get { return _tfpPath; }
- set { _tfpPath = value; RaisePropertyChangedAuto(); }
+ set { _tfpPath = value; RaisePropertyChangedAuto(); TfpPathChanged?.Invoke(this, new EventArgs()); }
}
private String _installerProject;
@@ -105,6 +106,28 @@ namespace Tango.PPC.Common.Publish
set { _synchronization = value; RaisePropertyChangedAuto(); }
}
+ private String _personalAccessToken;
+ public String PersonalAccessToken
+ {
+ get { return _personalAccessToken; }
+ set { _personalAccessToken = value; RaisePropertyChangedAuto(); }
+ }
+
+ private bool _createTag;
+ public bool CreateTag
+ {
+ get { return _createTag; }
+ set { _createTag = value; RaisePropertyChangedAuto(); }
+ }
+
+ private bool _autoCommitAndSync;
+ public bool AutoCommitAndPush
+ {
+ get { return _autoCommitAndSync; }
+ set { _autoCommitAndSync = value; RaisePropertyChangedAuto(); }
+ }
+
+
public PublishOptions()
{
BasePath = AppDomain.CurrentDomain.BaseDirectory + "..\\";
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteActions/IRemoteActionsService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteActions/IRemoteActionsService.cs
new file mode 100644
index 000000000..477663342
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteActions/IRemoteActionsService.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.RemoteActions
+{
+ public interface IRemoteActionsService
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/DefaultRemoteAssistanceProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/DefaultRemoteAssistanceProvider.cs
index eae13a882..c266ba7c0 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/DefaultRemoteAssistanceProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/DefaultRemoteAssistanceProvider.cs
@@ -96,8 +96,12 @@ namespace Tango.PPC.Common.RemoteAssistance
/// Installs the remote assistance.
/// </summary>
/// <param name="machineSerialNumber">The machine serial number.</param>
+ /// <param name="group">The remote assistance group.</param>
+ /// <param name="environment">The machine working environment.</param>
/// <returns></returns>
- public async Task InstallRemoteAssistance(String machineSerialNumber)
+ /// <exception cref="FileNotFoundException"></exception>
+ /// <exception cref="ApplicationException">The remote assistance service was installed but could not be found.</exception>
+ public async Task InstallRemoteAssistance(String machineSerialNumber, String group, String environment)
{
try
{
@@ -106,26 +110,21 @@ namespace Tango.PPC.Common.RemoteAssistance
throw new FileNotFoundException($"The remote assistance installer file could not be found at {_installer_path}.");
}
- if (!(await IsRemoteAssistanceInstalled()))
- {
- CmdCommand command = new CmdCommand("msiexec.exe", $"/i \"{_installer_path}\" /qn CUSTOMCONFIGID=ke43ann APITOKEN=4765529-gon1LwO1N1TTrlLI21ji ASSIGNMENTOPTIONS=\" --reassign --alias {"TANGO-" + machineSerialNumber} --grant-easy-access\"");
- command.Timeout = TimeSpan.FromSeconds(30);
- await command.Run();
+ String q = "\"";
+ //CmdCommand command = new CmdCommand("msiexec.exe", $"/i \"{_installer_path}\" /qn CUSTOMCONFIGID=ke43ann APITOKEN=4765529-gon1LwO1N1TTrlLI21ji ASSIGNMENTOPTIONS=\" --reassign --alias {"TANGO-" + machineSerialNumber}-{environment} --grant-easy-access\"");
+ CmdCommand command = new CmdCommand("msiexec.exe", $"/i {q}{_installer_path}{q} /qn CUSTOMCONFIGID=ke43ann APITOKEN=4765529-gon1LwO1N1TTrlLI21ji ASSIGNMENTOPTIONS={q} --group {q}{q} {group} {q}{q} --reassign --alias TANGO-{machineSerialNumber}-{environment} --grant-easy-access{q}");
+ command.Timeout = TimeSpan.FromSeconds(30);
+ await command.Run();
- bool exist = await IsRemoteAssistanceInstalled();
+ bool exist = await IsRemoteAssistanceInstalled();
- if (exist)
- {
- await DisableRemoteAssistance();
- }
- else
- {
- throw new ApplicationException("The remote assistance service was installed but could not be found.");
- }
+ if (exist)
+ {
+ await DisableRemoteAssistance();
}
else
{
- LogManager.Log("Remote assistance is already installed.");
+ throw new ApplicationException("The remote assistance service was installed but could not be found.");
}
}
catch (Exception ex)
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/IRemoteAssistanceProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/IRemoteAssistanceProvider.cs
index cafb8dab9..3b7d489e6 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/IRemoteAssistanceProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteAssistance/IRemoteAssistanceProvider.cs
@@ -32,7 +32,9 @@ namespace Tango.PPC.Common.RemoteAssistance
/// Installs the remote assistance.
/// </summary>
/// <param name="machineSerialNumber">The machine serial number.</param>
+ /// <param name="group">The remote assistance group.</param>
+ /// <param name="environment">The machine working environment.</param>
/// <returns></returns>
- Task InstallRemoteAssistance(String machineSerialNumber);
+ Task InstallRemoteAssistance(String machineSerialNumber, String group, String environment);
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs
new file mode 100644
index 000000000..8535d45d4
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs
@@ -0,0 +1,584 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Security.Authentication;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Integration.ExternalBridge;
+using Tango.Logging;
+using Tango.PPC.Common.Application;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.PPC.Common.OS;
+using Tango.RemoteDesktop;
+using Tango.RemoteDesktop.CaptureMethods;
+using Tango.RemoteDesktop.Encoders;
+using Tango.RemoteDesktop.Engines;
+using Tango.RemoteDesktop.Frames;
+using Tango.RemoteDesktop.Input;
+using Tango.RemoteDesktop.Network;
+using Tango.Settings;
+using Tango.Transport;
+using Tango.WebRTC;
+using static Tango.RemoteDesktop.Input.MouseController;
+
+namespace Tango.PPC.Common.RemoteDesktop
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultRemoteDesktopService : ExtendedObject, IRemoteDesktopService, IExternalBridgeRequestHandler
+ {
+ private RemoteDesktopPacket _initialPacket;
+ private RasterScreenCaptureEngine _engine;
+ private PPCSettings _settings;
+ private List<RemoteDesktopClient> _clients;
+ private JsonSerializerSettings _jsonSettings;
+ private IOperationSystemManager _osManager;
+ private IPPCApplicationManager _appManager;
+ private bool _drawCursor;
+ private bool _isMouseDown;
+ private bool _ensureMouseDown;
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this <see cref="IPPCService" /> is enabled.
+ /// </summary>
+ public bool Enabled { get; set; } = true;
+
+ private bool _isStarted;
+ /// <summary>
+ /// Gets a value indicating whether the remote desktop service has started.
+ /// </summary>
+ public bool IsStarted
+ {
+ get { return _isStarted; }
+ private set { _isStarted = value; RaisePropertyChangedAuto(); }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether there is any active remote desktop session with a remote peer.
+ /// </summary>
+ public bool InSession
+ {
+ get
+ {
+ return _clients.Count > 0;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultRemoteDesktopService"/> class.
+ /// </summary>
+ /// <param name="applicationManager">The application manager.</param>
+ /// <param name="externalBridge">The external bridge.</param>
+ /// <param name="osManager">The os manager.</param>
+ public DefaultRemoteDesktopService(IPPCApplicationManager applicationManager, IPPCExternalBridgeService externalBridge, IOperationSystemManager osManager)
+ {
+ _osManager = osManager;
+ _appManager = applicationManager;
+
+ _jsonSettings = new JsonSerializerSettings()
+ {
+ TypeNameHandling = TypeNameHandling.All
+ };
+
+ _settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+ Enabled = _settings.EnableRemoteDesktop;
+
+ applicationManager.ApplicationReady += ApplicationManager_ApplicationReady;
+
+ externalBridge.RegisterRequestHandler(this);
+
+ _clients = new List<RemoteDesktopClient>();
+ _engine = new RasterScreenCaptureEngine();
+
+ _engine.CaptureCursor = false;
+
+ _engine.FrameRate = Math.Min(Math.Max(_settings.RemoteDesktopFrameRate, 1), 20);
+ _engine.FrameReceived += _engine_FrameReceived;
+ }
+
+ private void ApplicationManager_ApplicationReady(object sender, EventArgs e)
+ {
+
+
+ var mainWindow = System.Windows.Application.Current.MainWindow;
+
+ if (mainWindow.WindowStyle != System.Windows.WindowStyle.None)
+ {
+ mainWindow.LocationChanged += (_, __) =>
+ {
+ _engine.CaptureRegion = new CaptureRegion()
+ {
+ Left = (int)mainWindow.Left + 10,
+ Top = (int)mainWindow.Top + 5,
+ Width = (int)mainWindow.Width - 20,
+ Height = (int)mainWindow.Height - 15
+ };
+ };
+
+ _engine.CaptureRegion = new CaptureRegion()
+ {
+ Left = (int)mainWindow.Left + 10,
+ Top = (int)mainWindow.Top + 5,
+ Width = (int)mainWindow.Width - 20,
+ Height = (int)mainWindow.Height - 15
+ };
+ }
+ else
+ {
+ //DirectX capturing is not working on PPC !! Maybe when we replace ?
+ //try
+ //{
+ // _engine.CaptureMethod = new DirectXScreenCapture();
+ //}
+ //catch (Exception ex)
+ //{
+ // LogManager.Log(ex, "Could not initialize DirectX screen capture method on this device. Falling back to GDI.");
+ //}
+ }
+
+ mainWindow.PreviewMouseDown += (_, __) =>
+ {
+ _isMouseDown = true;
+ _ensureMouseDown = true;
+ };
+
+ mainWindow.PreviewMouseUp += (_, __) =>
+ {
+ _isMouseDown = false;
+ };
+
+ _engine.Comparer.MaxDifferencesThrow = _engine.CaptureRegion.Width * _engine.CaptureRegion.Height / 2;
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(StartRemoteDesktopSessionRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnStartRemoteDesktopSessionRequestReceived(StartRemoteDesktopSessionRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ var client = _clients.SingleOrDefault(x => x.Receiver == receiver);
+
+ if (client != null)
+ {
+ _clients.Remove(client);
+ }
+
+ RemoteDesktopClient newClient = new RemoteDesktopClient();
+ newClient.Receiver = receiver;
+ newClient.Token = token;
+ newClient.WebRtcClient = new WebRtcClient();
+ newClient.WebRtcClient.TextMessageReceived += WebRtcClient_TextMessageReceived;
+ _clients.Add(newClient);
+
+ await receiver.SendGenericResponse(new StartRemoteDesktopSessionResponse()
+ {
+ FrameRate = _engine.FrameRate
+ }, token);
+
+
+ if (!_engine.IsStarted)
+ {
+ _engine.Start();
+ }
+
+ RaisePropertyChanged(nameof(InSession));
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(WebRtcIceCandidateRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnWebRtcIceCandidateRequestReceived(WebRtcIceCandidateRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ var client = _clients.SingleOrDefault(x => x.Receiver == receiver);
+
+ if (client != null)
+ {
+ try
+ {
+ await receiver.SendGenericResponse(new WebRtcIceCandidateResponse() { }, token);
+ client.WebRtcClient.AddIceCandidate(request.IceCandidate);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error adding WebRTC ice candidate received from the remote connection.");
+ }
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(WebRtcOfferRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnWebRtcOfferRequestReceived(WebRtcOfferRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ var client = _clients.SingleOrDefault(x => x.Receiver == receiver);
+
+ if (client != null)
+ {
+ try
+ {
+
+ try
+ {
+ client.WebRtcClient.NewIceCandidate += async (x, e) =>
+ {
+ try
+ {
+ await receiver.SendGenericRequest<WebRtcIceCandidateRequest, WebRtcIceCandidateResponse>(new WebRtcIceCandidateRequest() { IceCandidate = e.IceCandidate });
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error sending ice candidate to remote peer.");
+ }
+ };
+ client.WebRtcClient.Ready += (x, e) =>
+ {
+ client.IsWebRtcReady = true;
+ };
+ client.WebRtcClient.Disconnected += (x, e) =>
+ {
+ client.IsWebRtcReady = false;
+ };
+
+ client.WebRtcClient.FrameWidth = 800;
+ client.WebRtcClient.FrameHeight = 1280;
+ client.WebRtcClient.FrameRate = _engine.FrameRate;
+
+ await client.WebRtcClient.Init();
+
+ var answer = await client.WebRtcClient.CreateAnswer(request.Offer);
+ await receiver.SendGenericResponse(new WebRtcOfferResponse() { Answer = answer }, token);
+ }
+ catch (Exception ex)
+ {
+ throw LogManager.Log(ex, "Error initializing the WebRTC client.");
+ }
+ }
+ catch (Exception ex)
+ {
+ throw LogManager.Log(ex, "Error responding to WebRTC offer request.");
+ }
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(StopRemoteDesktopSessionRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnStopRemoteDesktopSessionRequestReceived(StopRemoteDesktopSessionRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ var client = _clients.SingleOrDefault(x => x.Receiver == receiver);
+
+ if (client != null)
+ {
+ _clients.Remove(client);
+
+ if (client.WebRtcClient != null)
+ {
+ client.WebRtcClient.Dispose();
+ }
+ }
+
+ if (_clients.Count == 0)
+ {
+ _engine.Stop();
+ }
+
+ await receiver.SendGenericResponse(new StopRemoteDesktopSessionResponse(), token);
+
+ if (client != null)
+ {
+ await receiver.SendGenericResponse(new StartRemoteDesktopSessionResponse(), client.Token, new TransportResponseConfig() { Completed = true });
+ }
+
+ RaisePropertyChanged(nameof(InSession));
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(MouseStateRequest))]
+ public async Task OnMouseStateRequestReceived(MouseStateRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ MouseController.SetCursorPosition((int)request.Location.X, (int)request.Location.Y);
+
+ if (request.EventType == MouseEventType.Up || request.EventType == MouseEventType.Down)
+ {
+ MouseEventFlags flag = MouseEventFlags.LeftUp;
+
+ switch (request.EventType)
+ {
+ case MouseEventType.Down:
+ flag = request.Button == MouseButton.Right ? MouseEventFlags.RightDown : MouseEventFlags.LeftDown;
+ break;
+ case MouseEventType.Up:
+ flag = request.Button == MouseButton.Right ? MouseEventFlags.RightUp : MouseEventFlags.LeftUp;
+ break;
+ }
+
+ MouseController.MouseEvent(flag);
+ }
+ else if (request.EventType == MouseEventType.DoubleClick)
+ {
+ MouseController.DoubleClick();
+ }
+ else if (request.EventType == MouseEventType.Scroll)
+ {
+ MouseController.Scroll(request.ScrollDelta);
+ }
+
+ if (receiver != null)
+ {
+ await receiver.SendGenericResponse(new MouseStateResponse(), token);
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(TouchStateRequest))]
+ public async Task OnTouchStateRequestReceived(TouchStateRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ switch (request.EventType)
+ {
+ case TouchEventType.TouchDown:
+ TouchController.TouchDown((int)request.Location.X, (int)request.Location.Y);
+ break;
+ case TouchEventType.TouchMove:
+ TouchController.TouchMove(request.MoveDeltaX, request.MoveDeltaY);
+ break;
+ case TouchEventType.TouchUp:
+ TouchController.TouchUp();
+ break;
+ }
+
+ if (receiver != null)
+ {
+ await receiver.SendGenericResponse(new TouchStateResponse(), token);
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(KeyboardStateRequest))]
+ public async Task OnKeyboardStateRequestReceived(KeyboardStateRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ if (request.EventType == KeyboardEventType.Down)
+ {
+ KeyboardController.KeyDown(request.Key, request.IsCtrlDown, request.IsShiftDown, request.IsAltDown);
+ }
+ else
+ {
+ KeyboardController.KeyUp(request.Key, request.IsCtrlDown, request.IsShiftDown, request.IsAltDown);
+ }
+
+ if (receiver != null)
+ {
+ await receiver.SendGenericResponse(new KeyboardStateResponse(), token);
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteDesktopCommandRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRemoteDesktopCommandRequest(RemoteDesktopCommandRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ switch (request.Command)
+ {
+ case RemoteDesktopCommand.HideAndOpenShell:
+ _osManager.OpenShell();
+ _appManager.SetWindowState(System.Windows.WindowState.Minimized);
+ break;
+ }
+
+ await receiver.SendGenericResponse(new RemoteDesktopCommandResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(SetCursorVisibilityRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnSetCursorVisibilityRequest(SetCursorVisibilityRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ _drawCursor = request.Visible;
+ await receiver.SendGenericResponse(new SetCursorVisibilityResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(GetScreenshotRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnGetScreenshotRequest(GetScreenshotRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ GdiScreenCapture capture = new GdiScreenCapture();
+ var bitmap = capture.GetDesktopBitmap(CaptureRegion.PrimaryScreenBounds());
+ RasterFrame frame = new RasterFrame(bitmap);
+ var data = frame.ToEncoder<JpegEncoder>().ToArray(80);
+ frame.Dispose();
+
+ await receiver.SendGenericResponse(new GetScreenshotResponse() { Bitmap = data }, token);
+ }
+
+ private async void _engine_FrameReceived(object sender, ScreenCaptureFrameReceivedEventArgs<RasterFrame> e)
+ {
+ try
+ {
+ if (_drawCursor)
+ {
+ e.Frame.DrawImage((_isMouseDown || _ensureMouseDown) ? Properties.Resources.tap : Properties.Resources.finger3, new System.Drawing.Point(System.Windows.Forms.Cursor.Position.X - 5, System.Windows.Forms.Cursor.Position.Y - 4));
+ _ensureMouseDown = false;
+ }
+
+ _initialPacket = new RemoteDesktopPacket()
+ {
+ Bitmap = e.Frame.ToEncoder<PngEncoder>().ToArray(),
+ };
+
+ if (_clients.Count > 0)
+ {
+ bool useWebRTC = _clients.ToList().All(x => x.IsWebRtcReady);
+
+ if (useWebRTC)
+ {
+ _engine.EnableComparer = false;
+
+ foreach (var client in _clients.ToList())
+ {
+ try
+ {
+ client.WebRtcClient.PushFrame(e.Frame.ToBitmap());
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, LogCategory.Debug, "Error pushing remote desktop frame via WebRTC channel.");
+ }
+ }
+
+ e.Frame.Dispose();
+ }
+ else
+ {
+ _engine.EnableComparer = true;
+
+ foreach (var client in _clients.ToList().Where(x => !x.InitialPacketSent))
+ {
+ try
+ {
+ await client.Receiver.SendGenericResponse(new StartRemoteDesktopSessionResponse()
+ {
+ Packet = _initialPacket,
+ }, client.Token, new TransportResponseConfig()
+ {
+ Immediate = false,
+ });
+
+ client.InitialPacketSent = true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ }
+ }
+
+ if (e.Frame.DifferenceCount > 0)
+ {
+ RemoteDesktopPacket packet = null;
+
+ Point mousePosition = new Point(System.Windows.Forms.Cursor.Position.X, System.Windows.Forms.Cursor.Position.Y);
+
+ if (!e.Frame.DifferenceAvailable)
+ {
+ packet = new RemoteDesktopPacket()
+ {
+ Bitmap = e.Frame.ToEncoder<JpegEncoder>().ToArray(30),
+ MousePosition = mousePosition,
+ CursorVisible = _drawCursor
+ };
+ }
+ else
+ {
+ var diffFrame = e.Frame.ToDifference();
+ diffFrame = diffFrame.OptimizeBounds();
+
+ packet = new RemoteDesktopPacket()
+ {
+ Bitmap = diffFrame.ToEncoder<PngEncoder>().ToArray(),
+ IsPartial = true,
+ PartialRegion = new CaptureRegion(diffFrame.Left, diffFrame.Top, diffFrame.Width, diffFrame.Height),
+ MousePosition = mousePosition,
+ CursorVisible = _drawCursor
+ };
+
+ diffFrame.Dispose();
+ }
+
+ Debug.WriteLine($"Remote Desktop Bitmap Size: {packet.Bitmap.Length / 1000} kb");
+
+ foreach (var client in _clients.ToList().Where(x => x.InitialPacketSent))
+ {
+ try
+ {
+ await client.Receiver.SendGenericResponse(new StartRemoteDesktopSessionResponse()
+ {
+ Packet = packet
+ }, client.Token, new TransportResponseConfig()
+ {
+ Immediate = false,
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error occurred on remote desktop engine frame received event.");
+ }
+ finally
+ {
+ e.Frame.Dispose();
+ }
+ }
+
+ private async void WebRtcClient_TextMessageReceived(object sender, DataMessageReceivedEventArgs<string> e)
+ {
+ try
+ {
+ var request = JsonConvert.DeserializeObject(e.Data, _jsonSettings);
+
+ if (request.GetType() == typeof(MouseStateRequest))
+ {
+ await OnMouseStateRequestReceived(request as MouseStateRequest, null, null);
+ }
+ else if (request.GetType() == typeof(KeyboardStateRequest))
+ {
+ await OnKeyboardStateRequestReceived(request as KeyboardStateRequest, null, null);
+ }
+ else if (request.GetType() == typeof(TouchStateRequest))
+ {
+ await OnTouchStateRequestReceived(request as TouchStateRequest, null, null);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error deserializing incoming message on the WebRTC data Channel.");
+ }
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ var client = _clients.SingleOrDefault(x => x.Receiver == receiver);
+
+ if (client != null)
+ {
+ LogManager.Log("Remote desktop client disconnected. Disposing WebRTC channel...");
+
+ _clients.Remove(client);
+
+ try
+ {
+ if (client.WebRtcClient != null)
+ {
+ client.WebRtcClient.Dispose();
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error disposing the WebRTC channel.");
+ }
+ }
+
+ if (_clients.Count == 0)
+ {
+ _engine.Stop();
+ }
+
+ RaisePropertyChanged(nameof(InSession));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs
new file mode 100644
index 000000000..5e4a801d7
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.RemoteDesktop
+{
+ /// <summary>
+ /// Represents a PPC remote desktop service.
+ /// </summary>
+ public interface IRemoteDesktopService : IPPCService
+ {
+ /// <summary>
+ /// Gets a value indicating whether the remote desktop service has started.
+ /// </summary>
+ bool IsStarted { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether there is any active remote desktop session with a remote peer.
+ /// </summary>
+ bool InSession { get; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/RemoteDesktopClient.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/RemoteDesktopClient.cs
new file mode 100644
index 000000000..f0f0a87de
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/RemoteDesktopClient.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Integration.ExternalBridge;
+using Tango.WebRTC;
+
+namespace Tango.PPC.Common.RemoteDesktop
+{
+ public class RemoteDesktopClient
+ {
+ public String Token { get; set; }
+ public ExternalBridgeReceiver Receiver { get; set; }
+ public bool InitialPacketSent { get; set; }
+ public WebRtcClient WebRtcClient { get; set; }
+ public bool IsWebRtcReady { get; set; }
+ }
+
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteJob/DefaultRemoteJobService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteJob/DefaultRemoteJobService.cs
new file mode 100644
index 000000000..8826a8be3
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteJob/DefaultRemoteJobService.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.DTO;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Integration.ExternalBridge;
+using Tango.Integration.Operation;
+using Tango.PPC.Common.Connection;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.PPC.Shared.Jobs;
+
+namespace Tango.PPC.Common.RemoteJob
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultRemoteJobService : IRemoteJobService, IExternalBridgeRequestHandler
+ {
+ private class RunningJobUpdateClient
+ {
+ public String Token { get; set; }
+ public ExternalBridgeReceiver Receiver { get; set; }
+ public bool JobSent { get; set; }
+ }
+
+ private List<RunningJobUpdateClient> _clients;
+ private JobHandler _handler;
+ private JobDTO _currentJobDTO;
+ private ProcessParametersTableDTO _currentJobProcessParameters;
+
+ public bool Enabled { get; set; } = true;
+
+ private IMachineProvider MachineProvider { get; set; }
+
+ public DefaultRemoteJobService(IPPCExternalBridgeService externalBridge, IMachineProvider machineProvider)
+ {
+ externalBridge.RegisterRequestHandler(this);
+
+ MachineProvider = machineProvider;
+
+ _clients = new List<RunningJobUpdateClient>();
+ MachineProvider.MachineOperator.PrintingStarted += MachineOperator_PrintingStarted;
+ }
+
+ private async void MachineOperator_PrintingStarted(object sender, Integration.Operation.PrintingEventArgs e)
+ {
+ _handler = e.JobHandler;
+
+ e.JobHandler.StatusChanged += JobHandler_StatusChanged;
+ e.JobHandler.Stopped += JobHandler_Stopped;
+
+ _currentJobDTO = JobDTO.FromObservable(e.Job);
+ _currentJobProcessParameters = ProcessParametersTableDTO.FromObservable(_handler.ProcessParameters);
+
+ foreach (var client in _clients.ToList())
+ {
+ try
+ {
+ RemoteJobProgress progress = new RemoteJobProgress();
+ progress.Stage = RemoteJobStage.Started;
+ progress.JobStatus = _handler.JobStatus;
+
+ await client.Receiver.SendGenericResponse(new RemoteJobUpdateResponse()
+ {
+ Job = _currentJobDTO,
+ ProcessParameters = _currentJobProcessParameters,
+ Progress = progress
+ }, client.Token);
+
+ client.JobSent = true;
+ }
+ catch { }
+ }
+ }
+
+ private async void JobHandler_StatusChanged(object sender, RunningJobStatus e)
+ {
+ foreach (var client in _clients.ToList())
+ {
+ if (client.JobSent)
+ {
+ try
+ {
+ await client.Receiver.SendGenericResponse(new RemoteJobUpdateResponse()
+ {
+ Progress = GetJobProgress(_handler)
+ }, client.Token);
+ }
+ catch { }
+ }
+ else
+ {
+ try
+ {
+ RemoteJobProgress progress = new RemoteJobProgress();
+ progress.Stage = RemoteJobStage.Started;
+ progress.JobStatus = _handler.JobStatus;
+
+ await client.Receiver.SendGenericResponse(new RemoteJobUpdateResponse()
+ {
+ Job = _currentJobDTO,
+ ProcessParameters = _currentJobProcessParameters,
+ Progress = progress
+ }, client.Token);
+
+ client.JobSent = true;
+ }
+ catch { }
+ }
+ }
+ }
+
+ private async void JobHandler_Stopped(object sender, EventArgs e)
+ {
+ foreach (var client in _clients.ToList().Where(x => x.JobSent))
+ {
+ try
+ {
+ await client.Receiver.SendGenericResponse(new RemoteJobUpdateResponse()
+ {
+ Progress = GetJobProgress(_handler),
+ }, client.Token);
+
+ client.JobSent = false;
+ }
+ catch { }
+ }
+ }
+
+ private RemoteJobProgress GetJobProgress(JobHandler handler)
+ {
+ RemoteJobStage stage = RemoteJobStage.Running;
+
+ if (_handler.Status.IsCanceled)
+ {
+ stage = RemoteJobStage.Aborted;
+ }
+ else if (_handler.Status.IsCompleted)
+ {
+ stage = RemoteJobStage.Completed;
+ }
+ else if (_handler.Status.IsFailed)
+ {
+ stage = RemoteJobStage.Failed;
+ }
+
+ return new RemoteJobProgress()
+ {
+ Stage = stage,
+ JobStatus = handler.JobStatus,
+ };
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(RemoteJobUpdateRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnRunningJobUpdateRequest(RemoteJobUpdateRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ if (!_clients.ToList().Exists(x => x.Receiver == receiver))
+ {
+ _clients.Add(new RunningJobUpdateClient()
+ {
+ Receiver = receiver,
+ Token = token
+ });
+ }
+
+ await receiver.SendGenericResponse(new RemoteJobUpdateResponse(), token);
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+ _clients.RemoveAll(x => x.Receiver == receiver);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteJob/IRemoteJobService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteJob/IRemoteJobService.cs
new file mode 100644
index 000000000..e7bfdbec1
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteJob/IRemoteJobService.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Integration.ExternalBridge;
+
+namespace Tango.PPC.Common.RemoteJob
+{
+ public interface IRemoteJobService : IPPCService
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Colors.xaml b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Colors.xaml
index 03f4b6f36..5fdbcf5f8 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Colors.xaml
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Colors.xaml
@@ -2,6 +2,17 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Tango.PPC.Common.Resources">
+ <BitmapImage x:Key="TangoImageLubricant" UriSource="../Images/lubricant2.png" />
+ <BitmapImage x:Key="TangoImageCleaner" UriSource="../Images/cl-full.png" />
+ <SolidColorBrush x:Key="TangoBlackInkBrush" Color="Black" />
+ <SolidColorBrush x:Key="TangoMagentaInkBrush" Color="#ED008C" />
+ <SolidColorBrush x:Key="TangoCyanInkBrush" Color="#1662EB" />
+ <SolidColorBrush x:Key="TangoYellowInkBrush" Color="#E8FF0C" />
+ <SolidColorBrush x:Key="TangoWasteBrush" Color="#2BA221" />
+ <SolidColorBrush x:Key="TangoTransparentInkBrush" Color="#E9E9E9" />
+ <SolidColorBrush x:Key="TangoLubricantBrush" Color="#DEDAC4" />
+ <SolidColorBrush x:Key="TangoCleanerBrush" Color="#8BEC83" />
+
</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Merged.xaml b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Merged.xaml
index 730066b46..09762d7ce 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Merged.xaml
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Merged.xaml
@@ -21,6 +21,9 @@
<ResourceDictionary Source="pack://application:,,,/Tango.PPC.Common;component/Resources/Fonts.xaml"/>
<ResourceDictionary Source="pack://application:,,,/Tango.PPC.Common;component/Resources/Styles.xaml"/>
+ <!--PPC Controls-->
+ <ResourceDictionary Source="pack://application:,,,/Tango.PPC.Common;component/Controls/ImageGalleryControl.xaml"/>
+
<!--Converters-->
</ResourceDictionary.MergedDictionaries>
@@ -51,6 +54,13 @@
<converters:StringToLinesConverter x:Key="StringToLinesConverter" />
<converters:ColorToIntegerConverter x:Key="ColorToIntegerConverter" />
<converters:StringEllipsisConverter x:Key="StringEllipsisConverter" />
+ <converters:DateTimeUTCToShortDateTimeConverter x:Key="DateTimeUTCToShortDateTimeConverter" />
+ <converters:TimeSpanToMinutesConverter x:Key="TimeSpanToMinutesConverter" />
+ <converters:TimeSpanToSecondsConverter x:Key="TimeSpanToSecondsConverter" />
+ <converters:TimeSpanToDaysConverter x:Key="TimeSpanToDaysConverter" />
+ <converters:BooleanToYesNoConverter x:Key="BooleanToYesNoConverter" />
+ <converters:IsEqualToVisibilityConverter x:Key="IsEqualToVisibilityConverter" />
+ <converters:IsToStringEqualToVisibilityConverter x:Key="IsToStringEqualToVisibilityConverter" />
<Style TargetType="FrameworkElement">
<Setter Property="TextElement.FontFamily" Value="{StaticResource TangoFlexoFontFamily}"></Setter>
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Styles.xaml b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Styles.xaml
index 426372688..317b2f13f 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Styles.xaml
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/Styles.xaml
@@ -44,4 +44,69 @@
<Setter Property="ItemContainerStyle" Value="{StaticResource BlankListBoxItem}"></Setter>
</Style>
+ <Style TargetType="touch:TouchSimpleDataGrid" x:Key="TechGrid" BasedOn="{StaticResource {x:Type touch:TouchSimpleDataGrid}}">
+ <Style.Resources>
+ <Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}" TargetType="{x:Type DataGridColumnHeader}">
+ <Setter Property="Background" Value="{StaticResource TangoDarkForegroundBrush}" />
+ <Setter Property="Foreground" Value="{StaticResource TangoLightForegroundBrush}" />
+ <Setter Property="Padding" Value="5"></Setter>
+ </Style>
+ </Style.Resources>
+ <Setter Property="RowStyle">
+ <Setter.Value>
+ <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
+ <Style.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter Property="Background" Value="Transparent"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource TangoPrimaryAccentBrush}" />
+ </Trigger>
+ <Trigger Property="IsFocused" Value="True">
+ <Setter Property="Background" Value="Transparent"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource TangoPrimaryAccentBrush}" />
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="CellStyle">
+ <Setter.Value>
+ <Style TargetType="{x:Type DataGridCell}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type DataGridCell}">
+ <Grid Background="{TemplateBinding Background}">
+ <ContentPresenter VerticalAlignment="Center" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter Property="Background" Value="Transparent"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource TangoPrimaryAccentBrush}" />
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+ </Setter.Value>
+ </Setter>
+
+ <Setter Property="Background" Value="{StaticResource TangoPrimaryBackgroundBrush}"></Setter>
+ <Setter Property="AutoGenerateColumns" Value="False"></Setter>
+ <Setter Property="SelectionUnit" Value="FullRow"></Setter>
+ <Setter Property="SelectionMode" Value="Single"></Setter>
+ <Setter Property="BorderBrush" Value="{StaticResource TangoDarkForegroundBrush}"></Setter>
+ <Setter Property="BorderThickness" Value="1"></Setter>
+ <Setter Property="HeadersVisibility" Value="Column"></Setter>
+ <Setter Property="CanUserAddRows" Value="True"></Setter>
+ <Setter Property="CanUserDeleteRows" Value="False"></Setter>
+ <Setter Property="CanUserReorderColumns" Value="False"></Setter>
+ <Setter Property="CanUserResizeColumns" Value="False"></Setter>
+ <Setter Property="CanUserSortColumns" Value="False"></Setter>
+ <Setter Property="IsReadOnly" Value="True"></Setter>
+ <Setter Property="VerticalGridLinesBrush" Value="{x:Null}"></Setter>
+ <Setter Property="HorizontalGridLinesBrush" Value="{StaticResource TangoGrayBrush}"></Setter>
+ <Setter Property="RowHeight" Value="50"></Setter>
+ <Setter Property="VerticalScrollBarVisibility" Value="Visible"></Setter>
+ <Setter Property="HorizontalScrollBarVisibility" Value="Disabled"></Setter>
+ </Style>
</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/finger3.png b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/finger3.png
new file mode 100644
index 000000000..c0a6ce9cd
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/finger3.png
Binary files differ
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/tap.png b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/tap.png
new file mode 100644
index 000000000..4fa679b81
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Resources/tap.png
Binary files differ
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/SQL/DefaultRemoteSqlService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/SQL/DefaultRemoteSqlService.cs
new file mode 100644
index 000000000..e5ac43d3f
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/SQL/DefaultRemoteSqlService.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL;
+using Tango.Core.DI;
+using Tango.Integration.ExternalBridge;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.PPC.Shared.SQL;
+
+namespace Tango.PPC.Common.SQL
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultRemoteSqlService : IRemoteSqlService, IExternalBridgeRequestHandler
+ {
+ public bool Enabled { get; set; } = true;
+
+ public DefaultRemoteSqlService(IPPCExternalBridgeService externalBridge)
+ {
+ externalBridge.RegisterRequestHandler(this);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(ExecuteSqlRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
+ public async Task OnExecuteSqlRequest(ExecuteSqlRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ this.ThrowIfDisabled();
+
+ RemoteSqlDataSet dataSet = new RemoteSqlDataSet();
+ int affected = 0;
+
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ using (SqlConnection connection = new SqlConnection(db.Database.Connection.ConnectionString))
+ {
+ SqlCommand command = new SqlCommand(request.SQL, connection);
+ connection.Open();
+
+ SqlDataReader reader = command.ExecuteReader();
+ affected = reader.RecordsAffected;
+
+ dataSet = await RemoteSqlDataSet.Load(reader);
+ }
+ }
+
+ await receiver.SendGenericResponse(new ExecuteSqlResponse()
+ {
+ DataSet = dataSet,
+ AffectedRecords = affected
+ }, token);
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/SQL/IRemoteSqlService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/SQL/IRemoteSqlService.cs
new file mode 100644
index 000000000..f70589090
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/SQL/IRemoteSqlService.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.SQL
+{
+ public interface IRemoteSqlService : IPPCService
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/SafetyLevelOperations.csv b/Software/Visual_Studio/PPC/Tango.PPC.Common/SafetyLevelOperations.csv
new file mode 100644
index 000000000..e8fe002b6
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/SafetyLevelOperations.csv
@@ -0,0 +1,3 @@
+MessageType,
+MotorHomingRequest,
+MotorJoggingRequest \ No newline at end of file
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/DefaultStorageProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/DefaultStorageProvider.cs
index 46315e4b8..5f097d303 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/DefaultStorageProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/DefaultStorageProvider.cs
@@ -21,7 +21,7 @@ namespace Tango.PPC.Common.Storage
public class DefaultStorageProvider : ExtendedObject, IStorageProvider
{
private Thread _scanThread;
- private Dictionary<String, Action<ExplorerFileItem>> _fileHandlers;
+ private Dictionary<String, Action<List<ExplorerFileItem>>> _fileHandlers;
/// <summary>
/// Occurs when a new storage drive has been inserted.
@@ -58,7 +58,7 @@ namespace Tango.PPC.Common.Storage
/// </summary>
public DefaultStorageProvider(IPPCApplicationManager applicationManager)
{
- _fileHandlers = new Dictionary<string, Action<ExplorerFileItem>>();
+ _fileHandlers = new Dictionary<string, Action<List<ExplorerFileItem>>>();
var drives = DriveInfo.GetDrives().Where(x => x.DriveType == DriveType.Removable).ToList();
if (drives.Count > 0)
@@ -86,7 +86,7 @@ namespace Tango.PPC.Common.Storage
/// <param name="extension">The file extension.</param>
/// <param name="handler">The handler.</param>
/// <exception cref="System.InvalidOperationException">Cannot register multiple file handlers for the same extension.</exception>
- public void RegisterFileHandler(string extension, Action<ExplorerFileItem> handler)
+ public void RegisterFileHandler(string extension, Action<List<ExplorerFileItem>> handler)
{
if (_fileHandlers.ContainsKey(extension))
{
@@ -99,7 +99,7 @@ namespace Tango.PPC.Common.Storage
/// Unregisters the file handler.
/// </summary>
/// <param name="handler">The handler.</param>
- public void UnregisterFileHandler(Action<ExplorerFileItem> handler)
+ public void UnregisterFileHandler(Action<List<ExplorerFileItem>> handler)
{
var h = _fileHandlers.SingleOrDefault(x => x.Value == handler);
@@ -112,14 +112,17 @@ namespace Tango.PPC.Common.Storage
/// <summary>
/// Submits a file selection.
/// </summary>
- /// <param name="fileItem">The file item.</param>
- public void SubmitFileSelection(ExplorerFileItem fileItem)
+ /// <param name="fileItems">The file item.</param>
+ public void SubmitFileSelection(List<ExplorerFileItem> fileItems)
{
- String extension = Path.GetExtension(fileItem.Path);
-
- if (_fileHandlers.ContainsKey(extension))
+ if (fileItems != null && fileItems.Count > 0)
{
- _fileHandlers[extension].Invoke(fileItem);
+ String extension = Path.GetExtension(fileItems.First().Path);
+
+ if (_fileHandlers.ContainsKey(extension))
+ {
+ _fileHandlers[extension].Invoke(fileItems);
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/IStorageProvider.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/IStorageProvider.cs
index 902021002..2a9cf4e90 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/IStorageProvider.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Storage/IStorageProvider.cs
@@ -38,18 +38,18 @@ namespace Tango.PPC.Common.Storage
/// </summary>
/// <param name="extension">The file extension.</param>
/// <param name="handler">The handler.</param>
- void RegisterFileHandler(String extension, Action<ExplorerFileItem> handler);
+ void RegisterFileHandler(String extension, Action<List<ExplorerFileItem>> handler);
/// <summary>
/// Unregisters the file handler.
/// </summary>
/// <param name="handler">The handler.</param>
- void UnregisterFileHandler(Action<ExplorerFileItem> handler);
+ void UnregisterFileHandler(Action<List<ExplorerFileItem>> handler);
/// <summary>
/// Submits a file selection.
/// </summary>
/// <param name="fileItem">The file item.</param>
- void SubmitFileSelection(ExplorerFileItem fileItem);
+ void SubmitFileSelection(List<ExplorerFileItem> fileItems);
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/DefaultMachineDataSynchronizer.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/DefaultMachineDataSynchronizer.cs
new file mode 100644
index 000000000..5a951e9fa
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/DefaultMachineDataSynchronizer.cs
@@ -0,0 +1,662 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Timers;
+using Tango.BL;
+using Tango.PPC.Common.Web;
+using System.Data.Entity;
+using Tango.BL.DTO;
+using Tango.PPC.Common.Connection;
+using Tango.BL.Builders;
+using Tango.Settings;
+using Tango.Core;
+using Tango.PPC.Common.Authentication;
+using Tango.Logging;
+using System.Diagnostics;
+using Tango.BL.Enumerations;
+using Tango.PPC.Common.Application;
+using Tango.Core.DI;
+
+namespace Tango.PPC.Common.Synchronization
+{
+ public class DefaultMachineDataSynchronizer : ExtendedObject, IMachineDataSynchronizer
+ {
+ private Timer _synchTimer;
+ private PPCWebClient client;
+ private IMachineProvider _machineProvider;
+ private IAuthenticationProvider _authenticationProvider;
+ private List<LogItemBase> _logs;
+ private bool _synchronizedOnce;
+
+ [TangoInject(TangoInjectMode.WhenAvailable)]
+ private IPPCApplicationManager _appManager;
+
+ public event EventHandler<SynchronizationStatusChangedEventArgs> CurrentStatusChanged;
+ public event EventHandler SynchronizationStarted;
+ public event EventHandler<SynchronizationEndedEventArgs> SynchronizationEnded;
+
+ public int MaxJobs { get; set; }
+ public int MaxJobRuns { get; set; }
+ public int MaxMachinesEvents { get; set; }
+ public int MaxOfflineUpdates { get; set; }
+ public int MaxDataStoreItems { get; set; }
+
+ private SynchronizationStatus _currentStatus;
+ public SynchronizationStatus CurrentStatus
+ {
+ get { return _currentStatus; }
+ private set { _currentStatus = value; RaisePropertyChangedAuto(); }
+ }
+
+ private SynchronizationStatus _lastStatus;
+ public SynchronizationStatus LastStatus
+ {
+ get { return _lastStatus; }
+ private set { _lastStatus = value; RaisePropertyChangedAuto(); }
+ }
+
+ public SynchronizedObservableCollection<SynchronizationStatus> StatusHistory { get; private set; }
+
+ public TimeSpan Interval { get; set; }
+
+ private bool _isEnabled;
+ public bool IsEnabled
+ {
+ get { return _isEnabled; }
+ set { _isEnabled = value; OnEnableChanged(); RaisePropertyChangedAuto(); }
+ }
+
+ private bool _isSynchronizing;
+ public bool IsSynchronizing
+ {
+ get { return _isSynchronizing; }
+ set { _isSynchronizing = value; RaisePropertyChangedAuto(); }
+ }
+
+ public DefaultMachineDataSynchronizer()
+ {
+ StatusHistory = new SynchronizedObservableCollection<SynchronizationStatus>();
+
+ MaxJobs = 10;
+ MaxJobRuns = 10;
+ MaxMachinesEvents = 10;
+ MaxOfflineUpdates = 10;
+ MaxDataStoreItems = 100;
+
+ var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+ Interval = settings.SynchronizationInterval;
+
+ _synchTimer = new Timer(Interval.TotalMilliseconds);
+ _synchTimer.Elapsed += _synchTimer_Elapsed;
+ _synchTimer.Enabled = true;
+
+ ExecuteNewStatus(TimeSpan.FromMinutes(2));
+ LastStatus = CurrentStatus;
+ }
+
+ public DefaultMachineDataSynchronizer(PPCWebClient ppcWebClient, IMachineProvider machineProvider, IAuthenticationProvider authenticationProvider) : this()
+ {
+ _logs = new List<LogItemBase>();
+ _machineProvider = machineProvider;
+ client = new PPCWebClient(ppcWebClient, TimeSpan.FromMinutes(10));
+ _authenticationProvider = authenticationProvider;
+
+ LogManager.NewLog += LogManager_NewLog;
+ }
+
+ private void LogManager_NewLog(object sender, LogItemBase e)
+ {
+ if (IsSynchronizing)
+ {
+ _logs.Add(e);
+ }
+ }
+
+ private String GetLogsStringAndClear()
+ {
+ String logsString = String.Join(Environment.NewLine, _logs.ToList().Select(x => x.ToString()));
+ _logs.Clear();
+ return logsString;
+ }
+
+ private void OnEnableChanged()
+ {
+ _synchTimer.Interval = Interval.TotalMilliseconds;
+
+ if (IsEnabled)
+ {
+ CurrentStatus.State = SynchronizationState.Pending;
+ }
+ else
+ {
+ CurrentStatus.State = SynchronizationState.Disabled;
+ }
+ }
+
+ private void _synchTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ _synchTimer.Interval = Interval.TotalMilliseconds;
+
+ try
+ {
+ Synchronize().GetAwaiter().GetResult();
+ }
+ catch { }
+ }
+
+ private async Task<UploadMachineDataRequest> CreateUploadMachineDataRequest(bool syncJobs, bool syncDiagnostics)
+ {
+ UploadMachineDataRequest request = new UploadMachineDataRequest();
+
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ if (syncJobs)
+ {
+ LogManager.Log("Checking Jobs...");
+
+ var jobs = await new JobsCollectionBuilder(db).Set(x => !x.IsSynchronized).WithSegments().WithBrushStops().Query(x => x.Take(MaxJobs).OrderByDescending(z => z.LastUpdated)).BuildListAsync();
+ List<JobDTO> dtos = new List<JobDTO>();
+
+ foreach (var job in jobs)
+ {
+ var dto = JobDTO.FromObservable(job);
+ request.Jobs.Add(dto);
+ }
+ }
+
+ if (syncDiagnostics)
+ {
+ LogManager.Log("Checking Job Runs...");
+
+ var jobRuns = await db.JobRuns.Where(x => !x.IsSynchronized).Take(MaxJobRuns).OrderByDescending(x => x.LastUpdated).ToListAsync();
+ List<JobRunDTO> dtos = new List<JobRunDTO>();
+
+ foreach (var jobRun in jobRuns)
+ {
+ var dto = JobRunDTO.FromObservable(jobRun);
+ request.JobRuns.Add(dto);
+ }
+ }
+
+ if (syncDiagnostics)
+ {
+ LogManager.Log("Checking Events...");
+
+ var machineEvents = await db.MachinesEvents.Where(x => !x.IsSynchronized).Take(MaxMachinesEvents).OrderByDescending(x => x.LastUpdated).ToListAsync();
+ List<MachinesEventDTO> dtos = new List<MachinesEventDTO>();
+
+ foreach (var machineEvent in machineEvents)
+ {
+ machineEvent.IsSynchronized = true;
+ var dto = MachinesEventDTO.FromObservable(machineEvent);
+ request.MachineEvents.Add(dto);
+ }
+ }
+
+ if (syncDiagnostics)
+ {
+ LogManager.Log("Checking Offline Updates...");
+
+ var tangoUpdates = await db.TangoUpdates.Where(x => !x.IsSynchronized && (x.Status == (int)TangoUpdateStatuses.OfflineUpdateCompleted || x.Status == (int)TangoUpdateStatuses.OfflineUpdateFailed || x.Status == (int)TangoUpdateStatuses.OfflineFirmwareUpgradeCompleted || x.Status == (int)TangoUpdateStatuses.OfflineFirmwareUpgradeFailed)).Take(MaxOfflineUpdates).OrderByDescending(x => x.LastUpdated).ToListAsync();
+ List<TangoUpdateDTO> dtos = new List<TangoUpdateDTO>();
+
+ foreach (var tangoUpdate in tangoUpdates)
+ {
+ tangoUpdate.IsSynchronized = true;
+ var dto = TangoUpdateDTO.FromObservable(tangoUpdate);
+ request.OfflineUpdates.Add(dto);
+ }
+ }
+
+ { //Always synchronize data store items
+ LogManager.Log("Checking Data Store Items...");
+
+ var dataStoreItems = await db.DataStoreItems.Where(x => !x.IsSynchronized).Take(MaxDataStoreItems).ToListAsync();
+ List<DataStoreItemDTO> dtos = new List<DataStoreItemDTO>();
+
+ foreach (var item in dataStoreItems)
+ {
+ item.IsSynchronized = true;
+ var dto = DataStoreItemDTO.FromObservable(item);
+ dto.MachineGuid = null;
+ request.DataStoreItems.Add(dto);
+ }
+ }
+ }
+
+ return request;
+ }
+
+ private async Task FinalizeMachineDataUpload(UploadMachineDataRequest request, UploadMachineDataResponse response)
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ //Finalize jobs
+ foreach (var job in request.Jobs)
+ {
+ var failedJob = response.FailedJobs.SingleOrDefault(x => x.Guid == job.Guid);
+
+ if (failedJob == null)
+ {
+ var dbJob = await db.Jobs.SingleOrDefaultAsync(x => x.Guid == job.Guid);
+ dbJob.IsSynchronized = true;
+ }
+ else
+ {
+ LogManager.Log($"Synchronization Error - Job '{job.Name}' cannot be stored on the server due to the following reason:\n{failedJob.Reason}", LogCategory.Error);
+ }
+ }
+
+ //Finalize job runs
+ foreach (var jobRun in request.JobRuns)
+ {
+ var failedJobRun = response.FailedJobRuns.SingleOrDefault(x => x.Guid == jobRun.Guid);
+
+ if (failedJobRun == null)
+ {
+ var dbJobRun = await db.JobRuns.SingleOrDefaultAsync(x => x.Guid == jobRun.Guid);
+ dbJobRun.IsSynchronized = true;
+ }
+ else
+ {
+ LogManager.Log($"Synchronization Error - JobRun '{jobRun.ID}' cannot be stored on the server due to the following reason:\n{failedJobRun.Reason}", LogCategory.Error);
+ }
+ }
+
+ //Finalize machine events
+ foreach (var machineEvent in request.MachineEvents)
+ {
+ var failedMachineEvent = response.FailedMachineEvents.SingleOrDefault(x => x.Guid == machineEvent.Guid);
+
+ if (failedMachineEvent == null)
+ {
+ var dbMachineEvent = await db.MachinesEvents.SingleOrDefaultAsync(x => x.Guid == machineEvent.Guid);
+ dbMachineEvent.IsSynchronized = true;
+ }
+ else
+ {
+ LogManager.Log($"Synchronization Error - Event '{machineEvent.ID}' cannot be stored on the server due to the following reason:\n{failedMachineEvent.Reason}", LogCategory.Error);
+ }
+ }
+
+ //Finalize tango updates
+ foreach (var tangoUpdate in request.OfflineUpdates)
+ {
+ var failedTangoUpdate = response.FailedOfflineUpdates.SingleOrDefault(x => x.Guid == tangoUpdate.Guid);
+
+ if (failedTangoUpdate == null)
+ {
+ var dbTangoUpdate = await db.TangoUpdates.SingleOrDefaultAsync(x => x.Guid == tangoUpdate.Guid);
+ dbTangoUpdate.IsSynchronized = true;
+ }
+ else
+ {
+ LogManager.Log($"Synchronization Error - TangoUpdate '{tangoUpdate.ID}' cannot be stored on the server due to the following reason:\n{failedTangoUpdate.Reason}", LogCategory.Error);
+ }
+ }
+
+ //Finalize data store items
+ foreach (var dataStoreItem in request.DataStoreItems)
+ {
+ var failedDataStoreItem = response.FailedDataStoreItems.SingleOrDefault(x => x.Guid == dataStoreItem.Guid);
+
+ if (failedDataStoreItem == null)
+ {
+ var dbDataStoreItem = await db.DataStoreItems.SingleOrDefaultAsync(x => x.Guid == dataStoreItem.Guid);
+ dbDataStoreItem.IsSynchronized = true;
+ }
+ else
+ {
+ LogManager.Log($"Synchronization Error - DataStoreItem '{dataStoreItem.Key}' cannot be stored on the server due to the following reason:\n{failedDataStoreItem.Reason}", LogCategory.Error);
+ }
+ }
+
+ await db.SaveChangesAsync();
+ }
+ }
+
+ private async Task<DownloadMachineDataResponse> DownloadMachineData(bool syncJobs, bool syncDiagnostics)
+ {
+ return await client.DownloadMachineData(new DownloadMachineDataRequest()
+ {
+ RequestJobs = syncJobs,
+ RequestJobRuns = syncDiagnostics,
+ RequestMachineEvents = syncDiagnostics,
+ RequestDataStoreItems = true,
+ MaxJobs = MaxJobs,
+ MaxJobRuns = MaxJobRuns,
+ MaxMachinesEvents = MaxMachinesEvents,
+ MaxDataStoreItems = MaxDataStoreItems,
+ });
+ }
+
+ private async Task<NotifyMachineDataDownloadCompletedRequest> InsertReplaceMachineData(DownloadMachineDataResponse response)
+ {
+ NotifyMachineDataDownloadCompletedRequest request = new NotifyMachineDataDownloadCompletedRequest();
+
+ //Insert/Replace Jobs.
+ if (response.Jobs.Count > 0)
+ {
+ LogManager.Log("Inserting/Replacing Jobs...");
+ }
+ foreach (var dto in response.Jobs)
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ try
+ {
+ var job = dto.ToObservable();
+
+ job.ID = 0;
+ job.UserGuid = null;
+ job.CustomerGuid = null;
+ job.IsSynchronized = true;
+
+ var existingJob = await db.Jobs.SingleOrDefaultAsync(x => x.Guid == job.Guid);
+
+ if (existingJob == null)
+ {
+ db.Jobs.Add(job);
+ await db.SaveChangesAsync();
+ }
+ else if (job.LastUpdated > existingJob.LastUpdated)
+ {
+ existingJob.Delete(db);
+ db.Jobs.Add(job);
+ await db.SaveChangesAsync();
+ }
+
+ request.SynchronizedJobs.Add(job.Guid);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log($"Synchronization Error - Job '{dto.Name}' cannot be stored locally due to the following reason:\n{ex.FlattenMessage()}", LogCategory.Error);
+ }
+ }
+ }
+
+ //Insert/Update Data Store Items.
+ if (response.DataStoreItems.Count > 0)
+ {
+ LogManager.Log("Inserting/Updating Data Store Items...");
+ }
+ foreach (var dto in response.DataStoreItems)
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ try
+ {
+ var dataStoreItem = dto.ToObservable();
+
+ dataStoreItem.ID = 0;
+ dataStoreItem.MachineGuid = null;
+ dataStoreItem.IsSynchronized = true;
+
+ var existingItem = db.DataStoreItems.SingleOrDefault(x => x.Guid == dataStoreItem.Guid);
+
+ if (existingItem == null)
+ {
+ db.DataStoreItems.Add(dataStoreItem);
+ db.SaveChanges();
+ }
+ else if (dataStoreItem.LastUpdated >= existingItem.LastUpdated)
+ {
+ existingItem.DataType = dataStoreItem.DataType;
+ existingItem.Value = dataStoreItem.Value;
+ existingItem.IsSynchronized = true;
+ existingItem.LastUpdated = dataStoreItem.LastUpdated;
+ db.SaveChanges();
+ }
+
+ request.SynchronizedDataStoreItems.Add(dataStoreItem.Guid);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log($"Synchronization Error - DataStoreItem '{dto.Key}' cannot be stored locally due to the following reason:\n{ex.FlattenMessage()}", LogCategory.Error);
+ }
+ }
+ }
+
+ //Insert JobRuns.
+ if (response.JobRuns.Count > 0)
+ {
+ LogManager.Log("Inserting/Replacing Job Runs...");
+ }
+ foreach (var dto in response.JobRuns)
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ try
+ {
+ var run = dto.ToObservable();
+ run.ID = 0;
+ run.IsSynchronized = true;
+
+ if (await db.JobRuns.SingleOrDefaultAsync(x => x.Guid == run.Guid) == null)
+ {
+ db.JobRuns.Add(run);
+ await db.SaveChangesAsync();
+ }
+
+ request.SynchronizedJobRuns.Add(run.Guid);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log($"Synchronization Error - JobRun '{dto.ID}' cannot be stored locally due to the following reason:\n{ex.FlattenMessage()}", LogCategory.Error);
+ }
+ }
+ }
+
+ //Insert MachineEvents.
+ if (response.MachineEvents.Count > 0)
+ {
+ LogManager.Log("Inserting/Replacing Events...");
+ }
+ foreach (var dto in response.MachineEvents)
+ {
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ try
+ {
+ var ev = dto.ToObservable();
+ ev.ID = 0;
+ ev.UserGuid = null;
+ ev.IsSynchronized = true;
+
+ if (await db.MachinesEvents.SingleOrDefaultAsync(x => x.Guid == ev.Guid) == null)
+ {
+ db.MachinesEvents.Add(ev);
+ await db.SaveChangesAsync();
+ }
+
+ request.SynchronizedMachineEvents.Add(ev.Guid);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log($"Synchronization Error - Event '{dto.ID}' cannot be stored locally due to the following reason:\n{ex.FlattenMessage()}", LogCategory.Error);
+ }
+ }
+ }
+
+ return request;
+ }
+
+ public async Task Synchronize()
+ {
+ _synchronizedOnce = true;
+
+ if (!IsEnabled || IsSynchronizing) return;
+
+ var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+
+ var syncJobs = settings.SynchronizeJobs;
+ var syncDiagnostics = settings.SynchronizeDiagnostics;
+
+ IsSynchronizing = true;
+ SynchronizationStarted?.Invoke(this, new EventArgs());
+
+ _logs.Clear();
+
+ _synchTimer.Stop();
+
+ LogManager.Log("Starting machine data synchronization...");
+ LogManager.Log($"Synchronization interval: {Interval}.");
+
+ CurrentStatus.StartDateTime = DateTime.Now;
+ UpdateCurrentStatus(SynchronizationState.Synchronizing, "Starting synchronization...");
+
+ Stopwatch watch = new Stopwatch();
+ watch.Start();
+
+ String notifyToken = null;
+
+ int newChangedJobs = 0;
+ int newJobRuns = 0;
+ int newMachineEvents = 0;
+
+ try
+ {
+ LogManager.Log("Authenticating with machine service...");
+
+ UpdateCurrentStatus(SynchronizationState.Synchronizing, "Authenticating with machine service...");
+
+ if (!this.client.IsAuthenticated)
+ {
+ await this.client.Login(new LoginRequest()
+ {
+ Mode = LoginMode.Machine,
+ MachineGuid = _machineProvider.Machine.Guid,
+ });
+ }
+
+ UpdateCurrentStatus(SynchronizationState.Synchronizing, "Preparing machine data for upload...");
+ LogManager.Log("Preparing machine data for upload...");
+ var request = await CreateUploadMachineDataRequest(syncJobs, syncDiagnostics);
+ request.ApplicationVersion = _appManager.Version.ToString();
+ request.FirmwareVersion = _appManager.FirmwareVersion.ToString();
+
+ UpdateCurrentStatus(SynchronizationState.Synchronizing, "Uploading machine data...");
+ LogManager.Log($"Uploading machine data:\nJobs: {request.Jobs.Count}\nJob Runs: {request.JobRuns.Count}\nEvents: {request.MachineEvents.Count}\nOffline Updates: {request.OfflineUpdates.Count}");
+ var response = await this.client.UploadMachineData(request);
+ notifyToken = response.NotifyCompletedToken;
+ LogManager.Log($"Upload response received:\nFailed Jobs: {response.FailedJobs.Count}\nFailed Job Runs: {response.FailedJobRuns.Count}\nFailed Events: {response.FailedMachineEvents.Count}\nFailed Offline Updates: {response.FailedOfflineUpdates.Count}");
+ LogManager.Log("Finalizing upload...");
+ UpdateCurrentStatus(SynchronizationState.Synchronizing, "Finalizing upload...");
+ await FinalizeMachineDataUpload(request, response);
+
+ UpdateCurrentStatus(SynchronizationState.Synchronizing, "Downloading machine data from service...");
+ LogManager.Log("Downloading machine data from server...");
+ var downloadResponse = await DownloadMachineData(syncJobs, syncDiagnostics);
+
+ newChangedJobs = downloadResponse.Jobs.Count;
+ newJobRuns = downloadResponse.JobRuns.Count;
+ newMachineEvents = downloadResponse.MachineEvents.Count;
+
+ LogManager.Log($"Download response received:\nJobs: {downloadResponse.Jobs.Count}\nJob Runs: {downloadResponse.JobRuns.Count}\nEvents: {downloadResponse.MachineEvents.Count}");
+ UpdateCurrentStatus(SynchronizationState.Synchronizing, "Updating local database...");
+ LogManager.Log("Updating local database...");
+ var notifyRequest = await InsertReplaceMachineData(downloadResponse);
+ LogManager.Log($"Finalizing download:\nSynchronized Jobs: {notifyRequest.SynchronizedJobs.Count}\nSynchronized Job Runs: {notifyRequest.SynchronizedJobRuns.Count}\nSynchronized Events: {notifyRequest.SynchronizedMachineEvents.Count}");
+ UpdateCurrentStatus(SynchronizationState.Synchronizing, "Finalizing download...");
+ var notifyResponse = await this.client.NotifyMachineDataDownloadCompleted(notifyRequest);
+
+ if (notifyToken != null)
+ {
+ try
+ {
+ await client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
+ {
+ Token = notifyToken,
+ Status = TangoUpdateStatuses.SynchronizationCompleted,
+ });
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Synchronization completed successfully but an error occurred when trying to notify about the completion.");
+ }
+ }
+
+ LogManager.Log("Machine data synchronization completed successfully.");
+ UpdateCurrentStatus(SynchronizationState.Completed, "Synchronization completed successfully.", null, watch.Elapsed);
+ }
+ catch (Exception ex)
+ {
+ if (notifyToken != null)
+ {
+ try
+ {
+ await client.NotifyUpdateCompleted(new MachineUpdateCompletedRequest()
+ {
+ Token = notifyToken,
+ Status = TangoUpdateStatuses.SynchronizationFailed,
+ FailedReason = ex.FlattenMessage(),
+ FailedLog = GetLogsStringAndClear(),
+ });
+ }
+ catch (Exception ee)
+ {
+ LogManager.Log(ee, "Synchronization completed successfully but an error occurred when trying to notify about the completion.");
+ }
+ }
+
+ UpdateCurrentStatus(SynchronizationState.Failed, "Synchronization failed.", ex.FlattenMessage(), watch.Elapsed);
+ throw LogManager.Log(ex, "Error occurred while synchronizing machine data.");
+ }
+ finally
+ {
+ watch.Stop();
+ LogManager.Log($"Synchronization duration: {watch.Elapsed}.");
+ LastStatus = CurrentStatus;
+ CreateNewStatus();
+ IsSynchronizing = false;
+ SynchronizationEnded?.Invoke(this, new SynchronizationEndedEventArgs()
+ {
+ NewChangedJobs = newChangedJobs,
+ NewJobRuns = newJobRuns,
+ NewMachineEvents = newMachineEvents,
+ });
+ }
+
+ _synchTimer.Start();
+ }
+
+ private void CreateNewStatus()
+ {
+ CurrentStatus = new SynchronizationStatus();
+ CurrentStatus.State = SynchronizationState.Pending;
+ CurrentStatus.StartDateTime = DateTime.Now.Add(Interval);
+ StatusHistory.Insert(0, CurrentStatus);
+ }
+
+ private async void ExecuteNewStatus(TimeSpan delay)
+ {
+ CurrentStatus = new SynchronizationStatus();
+ CurrentStatus.State = SynchronizationState.Pending;
+ CurrentStatus.StartDateTime = DateTime.Now.Add(delay);
+ StatusHistory.Insert(0, CurrentStatus);
+ await Task.Delay(delay);
+ try
+ {
+ if (!_synchronizedOnce)
+ {
+ await Synchronize();
+ }
+ }
+ catch { }
+ }
+
+ private void UpdateCurrentStatus(SynchronizationState state, String message, String errorReason = null, TimeSpan? duration = null)
+ {
+ CurrentStatus.State = state;
+ CurrentStatus.Message = message;
+ CurrentStatus.ErrorReason = errorReason;
+ CurrentStatus.Duration = duration;
+ CurrentStatusChanged?.Invoke(this, new SynchronizationStatusChangedEventArgs()
+ {
+ Status = CurrentStatus,
+ });
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/IMachineDataSynchronizer.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/IMachineDataSynchronizer.cs
new file mode 100644
index 000000000..bfd527a05
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/IMachineDataSynchronizer.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+
+namespace Tango.PPC.Common.Synchronization
+{
+ public interface IMachineDataSynchronizer
+ {
+ event EventHandler SynchronizationStarted;
+ event EventHandler<SynchronizationEndedEventArgs> SynchronizationEnded;
+ event EventHandler<SynchronizationStatusChangedEventArgs> CurrentStatusChanged;
+ int MaxJobs { get; set; }
+ int MaxJobRuns { get; set; }
+ int MaxMachinesEvents { get; set; }
+ SynchronizationStatus CurrentStatus { get; }
+ SynchronizationStatus LastStatus { get; }
+ SynchronizedObservableCollection<SynchronizationStatus> StatusHistory { get; }
+ TimeSpan Interval { get; set; }
+ bool IsEnabled { get; set; }
+ bool IsSynchronizing { get; }
+ Task Synchronize();
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationEndedEventArgs.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationEndedEventArgs.cs
new file mode 100644
index 000000000..4b8040e95
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationEndedEventArgs.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.Synchronization
+{
+ public class SynchronizationEndedEventArgs : EventArgs
+ {
+ public int NewChangedJobs { get; set; }
+ public int NewJobRuns { get; set; }
+ public int NewMachineEvents { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationState.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationState.cs
new file mode 100644
index 000000000..5797f449f
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationState.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.Synchronization
+{
+ public enum SynchronizationState
+ {
+ [Description("Pending...")]
+ Pending,
+ [Description("Synchronizing...")]
+ Synchronizing,
+ [Description("Failed")]
+ Failed,
+ [Description("Completed")]
+ Completed,
+ [Description("Disabled")]
+ Disabled
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatus.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatus.cs
new file mode 100644
index 000000000..5b1d5d1d2
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatus.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+
+namespace Tango.PPC.Common.Synchronization
+{
+ public class SynchronizationStatus : ExtendedObject
+ {
+ private SynchronizationState _state;
+ public SynchronizationState State
+ {
+ get { return _state; }
+ set { _state = value; RaisePropertyChangedAuto(); }
+ }
+
+ private String _message;
+ public String Message
+ {
+ get { return _message; }
+ set { _message = value; RaisePropertyChangedAuto(); }
+ }
+
+ private String _errorReason;
+ public String ErrorReason
+ {
+ get { return _errorReason; }
+ set { _errorReason = value; RaisePropertyChangedAuto(); }
+ }
+
+ private TimeSpan? _duration;
+ public TimeSpan? Duration
+ {
+ get { return _duration; }
+ set { _duration = value; RaisePropertyChangedAuto(); }
+ }
+
+ private DateTime _startDateTime;
+ public DateTime StartDateTime
+ {
+ get { return _startDateTime; }
+ set { _startDateTime = value; RaisePropertyChangedAuto(); }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatusChangedEventArgs.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatusChangedEventArgs.cs
new file mode 100644
index 000000000..1f0a9a27f
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatusChangedEventArgs.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.Synchronization
+{
+ public class SynchronizationStatusChangedEventArgs : EventArgs
+ {
+ public SynchronizationStatus Status { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/DefaultSystemInfoService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/DefaultSystemInfoService.cs
new file mode 100644
index 000000000..0c818483c
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/DefaultSystemInfoService.cs
@@ -0,0 +1,138 @@
+using Microsoft.WindowsAPICodePack.Net;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Integration.ExternalBridge;
+using Tango.PPC.Common.Application;
+using Tango.PPC.Common.Connectivity;
+using Tango.PPC.Common.ExternalBridge;
+using Tango.PPC.Shared.Information;
+using Tango.Settings;
+using Tango.SystemInfo;
+
+namespace Tango.PPC.Common.SystemInfo
+{
+ [TangoCreateWhenRegistered]
+ public class DefaultSystemInfoService : ExtendedObject, ISystemInfoService, IExternalBridgeRequestHandler
+ {
+ public bool Enabled { get; set; } = true;
+
+ private List<SystemObjectsCollection> _baseSystemInfo;
+ private IPPCApplicationManager _applicationManager;
+ private IConnectivityProvider _connectivityProvider;
+
+ public DefaultSystemInfoService(IPPCExternalBridgeService externalBridge, IPPCApplicationManager applicationManager, IConnectivityProvider connectivityProvider)
+ {
+ _applicationManager = applicationManager;
+ _connectivityProvider = connectivityProvider;
+ externalBridge.RegisterRequestHandler(this);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(GetMachineInformationRequest), RequestHandlerLoggingMode.LogRequestName)]
+ public async Task OnGetMachineInformationRequest(GetMachineInformationRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ if (_baseSystemInfo == null) //Create hardware info just once.
+ {
+ _baseSystemInfo = new List<SystemObjectsCollection>();
+
+ if (!Debugger.IsAttached)
+ {
+ _baseSystemInfo = SystemObjectsCollection.Create();
+ }
+ }
+
+ //Now always update the latest custom objects..
+ var system = _baseSystemInfo.ToList();
+
+ //Get the networks that are currently connected to
+ var connectedNetwork = NetworkListManager.GetNetworks(NetworkConnectivityLevels.Connected).FirstOrDefault();
+
+ var settings = SettingsManager.Default.GetOrCreate<PPCSettings>();
+
+ var applicationCollection = new SystemObjectsCollection();
+ applicationCollection.Name = "Application";
+ system.Insert(0, applicationCollection);
+
+ //Application.
+ applicationCollection.Objects.Add(new SystemObject()
+ {
+ Name = "Tango PPC",
+ Properties = new List<SystemObjectProperty>()
+ {
+ new SystemObjectProperty() { Name = "Version", Value = _applicationManager.Version.ToString(3) },
+ new SystemObjectProperty() { Name = "Build Date", Value = _applicationManager.BuildDate },
+ new SystemObjectProperty() { Name = "Previous Version", Value = settings.PreviousApplicationVersion },
+ new SystemObjectProperty() { Name = "Firmware Version", Value = _applicationManager.FirmwareVersion.ToStringSafe() },
+ new SystemObjectProperty() { Name = "Technician Mode", Value = _applicationManager.IsInTechnicianMode.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "After Update", Value = _applicationManager.IsAfterUpdate.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Startup Date", Value = _applicationManager.StartUpDate.ToString() },
+ },
+ });
+
+
+ //Network.
+ if (connectedNetwork != null)
+ {
+ applicationCollection.Objects.Add(new SystemObject()
+ {
+ Name = "Network",
+ Properties = new List<SystemObjectProperty>()
+ {
+ new SystemObjectProperty() { Name = "Network Name", Value = connectedNetwork.Name },
+ new SystemObjectProperty() { Name = "Category", Value = connectedNetwork.Category.ToString() },
+ new SystemObjectProperty() { Name = "Type", Value = connectedNetwork.Connectivity.ToString() },
+ new SystemObjectProperty() { Name = "Domain", Value = connectedNetwork.DomainType.ToString() },
+ new SystemObjectProperty() { Name = "Connected Time", Value = connectedNetwork.ConnectedTime.ToString() },
+ new SystemObjectProperty() { Name = "Internet Connection", Value = connectedNetwork.IsConnectedToInternet.ToStringYesNo() },
+ },
+ });
+ }
+
+ //Settings.
+ applicationCollection.Objects.Add(new SystemObject()
+ {
+ Name = "Settings",
+ Properties = new List<SystemObjectProperty>()
+ {
+ new SystemObjectProperty() { Name = "Application State", Value = settings.ApplicationState.ToString() },
+ new SystemObjectProperty() { Name = "Auto Update Check", Value = settings.AutoCheckForUpdates.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Auto Update Interval", Value = settings.AutoUpdateCheckInterval.ToString() },
+ new SystemObjectProperty() { Name = "Automatic Thread Loading", Value = settings.EnableAutomaticThreadLoading.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Firmware Logs Enabled", Value = settings.EnableEmbeddedDebugLogs.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Emergency Switch Enabled", Value = settings.EnableEmergencyNotifications.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Liquid Quantity Validation Enabled", Value = settings.EnableJobLiquidQuantityValidation.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Remote Assistance Enabled", Value = settings.EnableRemoteAssistance.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Remote Desktop Enabled", Value = settings.EnableRemoteDesktop.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Start in Technician Mode", Value = settings.EnableTechnicianModeByDefault.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Watchdog Enabled", Value = settings.EnableWatchDog.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Job Units Method", Value = settings.JobUnitsMethod.ToString() },
+ new SystemObjectProperty() { Name = "PowerUp Screen Enabled", Value = settings.DisplayPowerUpScreen.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Firmware COM Port", Value = settings.EmbeddedComPort.ToStringOrEmpty() },
+ new SystemObjectProperty() { Name = "Emergency COM Port", Value = settings.EmergencyComPort.ToStringOrEmpty() },
+ new SystemObjectProperty() { Name = "Job Upload Method", Value = settings.JobUploadStrategy.ToString() },
+ new SystemObjectProperty() { Name = "Diagnostics Synchronization", Value = settings.SynchronizeDiagnostics.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "Jobs Synchronization", Value = settings.SynchronizeJobs.ToStringYesNo() },
+ new SystemObjectProperty() { Name = "TCP Write Mode", Value = settings.TcpTransportAdapterWriteMode.ToString() },
+ }.OrderBy(x => x.Name).ToList(),
+ });
+
+ await receiver.SendGenericResponse(new GetMachineInformationResponse()
+ {
+ Package = new InformationPackage()
+ {
+ System = system,
+ }
+ }, token);
+ }
+
+ public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
+ {
+
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/ISystemInfoService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/ISystemInfoService.cs
new file mode 100644
index 000000000..47ea7bd4b
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/SystemInfo/ISystemInfoService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.SystemInfo
+{
+ /// <summary>
+ /// Represents a PPC system information service.
+ /// </summary>
+ /// <seealso cref="Tango.PPC.Common.IPPCService" />
+ public interface ISystemInfoService : IPPCService
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj
index d130a8002..9d39c96d9 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj
@@ -72,6 +72,10 @@
<Reference Include="Microsoft.Azure.Common.NetFramework, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.Azure.ResourceManager, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.SqlServer.AzureStorageEnum, Version=14.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualBasic" />
+ <Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\Microsoft.WindowsAPICodePack-Core.1.1.0.0\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
+ </Reference>
<Reference Include="Microsoft.WindowsAzure.Storage, Version=4.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
@@ -120,7 +124,15 @@
<Link>GlobalVersionInfo.cs</Link>
</Compile>
<Compile Include="ApplicationStates.cs" />
- <Compile Include="Backup\IBackupManager.cs" />
+ <Compile Include="BackupRestore\BackupMode.cs" />
+ <Compile Include="BackupRestore\BackupRestoreProgressEventArgs.cs" />
+ <Compile Include="BackupRestore\BackupFile.cs" />
+ <Compile Include="BackupRestore\BackupSettings.cs" />
+ <Compile Include="BackupRestore\BackupRestoreStage.cs" />
+ <Compile Include="BackupRestore\DefaultBackupManager.cs" />
+ <Compile Include="BackupRestore\IBackupManager.cs" />
+ <Compile Include="BackupRestore\RestoreResult.cs" />
+ <Compile Include="BackupRestore\RestoreSettings.cs" />
<Compile Include="Connection\DefaultMachineProvider.cs" />
<Compile Include="Connection\IMachineProvider.cs" />
<Compile Include="Connection\MachineOperatorChangedEventArgs.cs" />
@@ -131,23 +143,66 @@
<Compile Include="Connectivity\WiFiAuthentication.cs" />
<Compile Include="Connectivity\WiFiNetwork.cs" />
<Compile Include="Connectivity\IConnectivityProvider.cs" />
+ <Compile Include="Console\DefaultConsoleEngineService.cs" />
+ <Compile Include="Console\IConsoleEngineService.cs" />
<Compile Include="Controls\AsyncAdornerControl.cs" />
+ <Compile Include="Controls\ImageGalleryControl.cs" />
<Compile Include="Controls\TwineCatalogControl.xaml.cs">
<DependentUpon>TwineCatalogControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TwineCatalogRenderer.cs" />
<Compile Include="Converters\HeightToOpacityConverter.cs" />
+ <Compile Include="DataStore\DefaultDataStoreService.cs" />
+ <Compile Include="DataStore\IDataStoreService.cs" />
<Compile Include="ExtensionMethods\IListExtensions.cs" />
<Compile Include="ExtensionMethods\ObservableCollectionExtensions.cs" />
<Compile Include="ExternalBridge\IPPCExternalBridgeService.cs" />
<Compile Include="ExternalBridge\PPCExternalBridgeService.cs" />
+ <Compile Include="FileSystem\DefaultFileSystemService.cs" />
+ <Compile Include="FileSystem\FileSystemOperation.cs" />
+ <Compile Include="FileSystem\FileSystemOperationMode.cs" />
+ <Compile Include="FileSystem\IFileSystemService.cs" />
+ <Compile Include="Helpers\KeyboardHelper.cs" />
+ <Compile Include="Helpers\LogsHelper.cs" />
<Compile Include="HotSpot\DefaultHotSpotProvider.cs" />
<Compile Include="HotSpot\IHotSpotProvider.cs" />
+ <Compile Include="Insights\DefaultInsightsService.cs" />
+ <Compile Include="Insights\IInsightsService.cs" />
<Compile Include="IPPCView.cs" />
+ <Compile Include="IPPCService.cs" />
<Compile Include="MachineSetup\IMachineSetupManager.cs" />
<Compile Include="MachineSetup\MachineSetupManager.cs" />
<Compile Include="MachineSetup\MachineSetupProgress.cs" />
<Compile Include="MachineSetup\MachineSetupResult.cs" />
+ <Compile Include="Notifications\AppBarPriority.cs" />
+ <Compile Include="Performance\DefaultPerformanceService.cs" />
+ <Compile Include="Performance\IPerformanceService.cs" />
+ <Compile Include="RemoteDesktop\DefaultRemoteDesktopService.cs" />
+ <Compile Include="RemoteDesktop\IRemoteDesktopService.cs" />
+ <Compile Include="RemoteDesktop\RemoteDesktopClient.cs" />
+ <Compile Include="RemoteJob\DefaultRemoteJobService.cs" />
+ <Compile Include="RemoteJob\IRemoteJobService.cs" />
+ <Compile Include="RemoteActions\IRemoteActionsService.cs" />
+ <Compile Include="SQL\DefaultRemoteSqlService.cs" />
+ <Compile Include="SQL\IRemoteSqlService.cs" />
+ <Compile Include="Synchronization\DefaultMachineDataSynchronizer.cs" />
+ <Compile Include="Synchronization\IMachineDataSynchronizer.cs" />
+ <Compile Include="Synchronization\SynchronizationEndedEventArgs.cs" />
+ <Compile Include="Synchronization\SynchronizationState.cs" />
+ <Compile Include="Synchronization\SynchronizationStatus.cs" />
+ <Compile Include="Synchronization\SynchronizationStatusChangedEventArgs.cs" />
+ <Compile Include="SystemInfo\DefaultSystemInfoService.cs" />
+ <Compile Include="SystemInfo\ISystemInfoService.cs" />
+ <Compile Include="ThreadLoading\IThreadLoadingService.cs" />
+ <Compile Include="UpdatePackages\DefaultPackageRunner.cs" />
+ <Compile Include="UpdatePackages\IPackageRunner.cs" />
+ <Compile Include="UpdatePackages\IPPCPackage.cs" />
+ <Compile Include="UpdatePackages\PackageContext.cs" />
+ <Compile Include="UpdatePackages\PackageProgressEventArgs.cs" />
+ <Compile Include="UpdatePackages\PackageRunnerResult.cs" />
+ <Compile Include="UpdatePackages\PackageStateChangedEventArgs.cs" />
+ <Compile Include="UpdatePackages\PackagesFile.cs" />
+ <Compile Include="UpdatePackages\PPCPackageAttribute.cs" />
<Compile Include="Publish\PPCPublisher.cs" />
<Compile Include="Publish\PublishInfo.cs" />
<Compile Include="Publish\PublishOptions.cs" />
@@ -158,12 +213,16 @@
<Compile Include="Web\CheckForUpdateRequest.cs" />
<Compile Include="Web\CheckForUpdateResponse.cs" />
<Compile Include="MachineUpdate\DbCompareResult.cs" />
+ <Compile Include="Web\NotifyMachineDataDownloadCompletedResponse.cs" />
+ <Compile Include="Web\NotifyMachineDataDownloadCompletedRequest.cs" />
<Compile Include="Web\DownloadUpdateRequest.cs" />
+ <Compile Include="Web\MachineUpdateCompletedResponse.cs" />
<Compile Include="Web\DownloadUpdateResponse.cs" />
<Compile Include="MachineUpdate\IMachineUpdateManager.cs" />
<Compile Include="Web\LoginMode.cs" />
<Compile Include="Web\LoginRequest.cs" />
<Compile Include="Web\LoginResponse.cs" />
+ <Compile Include="Web\MachineUpdateCompletedRequest.cs" />
<Compile Include="Web\MachineSetupRequest.cs" />
<Compile Include="Web\MachineSetupResponse.cs" />
<Compile Include="MachineUpdate\MachineUpdateProgress.cs" />
@@ -171,9 +230,9 @@
<Compile Include="MachineUpdate\MachineUpdateResult.cs" />
<Compile Include="Web\PPCWebClient.cs" />
<Compile Include="Web\PPCWebClientBase.cs" />
+ <Compile Include="Web\SynchronizationFailedEntity.cs" />
<Compile Include="Web\UpdateDBRequest.cs" />
<Compile Include="Web\UpdateDBResponse.cs" />
- <Compile Include="MachineUpdate\UpdatePackageFile.cs" />
<Compile Include="Messages\JobRemovedMessage.cs" />
<Compile Include="Messages\JobSavedMessage.cs" />
<Compile Include="Messages\MachineSettingsSavedMessage.cs" />
@@ -205,8 +264,13 @@
<Compile Include="Web\MachineVersionsResponse.cs" />
<Compile Include="Web\LatestVersionRequest.cs" />
<Compile Include="Web\LatestVersionResponse.cs" />
+ <Compile Include="Web\UpdatedEntity.cs" />
+ <Compile Include="Web\DownloadMachineDataRequest.cs" />
+ <Compile Include="Web\DownloadMachineDataResponse.cs" />
+ <Compile Include="Web\UploadMachineDataResponse.cs" />
<Compile Include="Web\UploadCompletedResponse.cs" />
<Compile Include="Web\UploadCompletedRequest.cs" />
+ <Compile Include="Web\UploadMachineDataRequest.cs" />
<Compile Include="Web\UploadVersionRequest.cs" />
<Compile Include="Web\UploadVersionResponse.cs" />
<Compile Include="UWF\DefaultUnifiedWriteFilterManager.cs" />
@@ -217,6 +281,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
+ <Page Include="Controls\ImageGalleryControl.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
<Page Include="Controls\MultiPieChart.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -293,8 +361,29 @@
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
+ <EmbeddedResource Include="SafetyLevelOperations.csv" />
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\..\DataStore\Tango.DataStore.Editing\Tango.DataStore.Editing.csproj">
+ <Project>{ee088ff7-04d1-41fb-9d6a-cedeee7a7b9c}</Project>
+ <Name>Tango.DataStore.Editing</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\DataStore\Tango.DataStore.EF\Tango.DataStore.EF.csproj">
+ <Project>{88d9906b-8fc4-4fe0-b7eb-127a0a8fcee4}</Project>
+ <Name>Tango.DataStore.EF</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\DataStore\Tango.DataStore.LiteDB\Tango.DataStore.Lite.csproj">
+ <Project>{fa96bc0c-4055-475c-9dcc-70a5a9436b10}</Project>
+ <Name>Tango.DataStore.Lite</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\DataStore\Tango.DataStore.Remote\Tango.DataStore.Remote.csproj">
+ <Project>{29448f3c-9b3e-4da6-8555-46a8b9a6b3aa}</Project>
+ <Name>Tango.DataStore.Remote</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\DataStore\Tango.DataStore\Tango.DataStore.csproj">
+ <Project>{e0364dfa-0721-4637-9d32-9d22aac109d6}</Project>
+ <Name>Tango.DataStore</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Tango.AdvancedInstaller\Tango.AdvancedInstaller.csproj">
<Project>{c5df1816-34e5-4700-824c-29623a1baa22}</Project>
<Name>Tango.AdvancedInstaller</Name>
@@ -307,10 +396,18 @@
<Project>{b4fe6485-4161-4b36-bc08-67e0b53d01b7}</Project>
<Name>Tango.ColorConversion</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\Tango.Console\Tango.Console.csproj">
+ <Project>{199e8359-cad3-433d-9eed-2027652b24a4}</Project>
+ <Name>Tango.Console</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Tango.Core\Tango.Core.csproj">
<Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project>
<Name>Tango.Core</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\Tango.CSV\Tango.CSV.csproj">
+ <Project>{58e8825f-0c96-449c-b320-1e82b0aa876b}</Project>
+ <Name>Tango.CSV</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Tango.Emulations\Tango.Emulations.csproj">
<Project>{63561e19-ff5a-414b-a5ef-e30711543e1d}</Project>
<Name>Tango.Emulations</Name>
@@ -319,6 +416,18 @@
<Project>{4399AF76-DB52-4CFB-8020-6F85BDB29FD5}</Project>
<Name>Tango.Explorer</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\Tango.FileSystem\Tango.FileSystem.csproj">
+ <Project>{c6ebbbbe-2123-44dc-aef7-a0d47d736ac0}</Project>
+ <Name>Tango.FileSystem</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Tango.Git\Tango.Git.csproj">
+ <Project>{99081c0e-065c-4d68-bf60-f82330cca02d}</Project>
+ <Name>Tango.Git</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Tango.Insights\Tango.Insights.csproj">
+ <Project>{4a55c185-3f8d-41b0-8815-c15f6213a14a}</Project>
+ <Name>Tango.Insights</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Tango.Integration\Tango.Integration.csproj">
<Project>{4206ac58-3b57-4699-8835-90bf6db01a61}</Project>
<Name>Tango.Integration</Name>
@@ -331,6 +440,10 @@
<Project>{e4927038-348d-4295-aaf4-861c58cb3943}</Project>
<Name>Tango.PMR</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\Tango.RemoteDesktop\Tango.RemoteDesktop.csproj">
+ <Project>{a78068d4-2061-4376-8ede-583d8d880dec}</Project>
+ <Name>Tango.RemoteDesktop</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Tango.Settings\Tango.Settings.csproj">
<Project>{d8f1ad85-526a-4f50-b6dc-d437af63d8d8}</Project>
<Name>Tango.Settings</Name>
@@ -343,6 +456,10 @@
<Project>{e1e66ed9-597d-45fa-8048-de90a6930484}</Project>
<Name>Tango.SQLExaminer</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\Tango.SystemInfo\Tango.SystemInfo.csproj">
+ <Project>{997a961c-beda-4b56-aa0f-c39e532f7ffa}</Project>
+ <Name>Tango.SystemInfo</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Tango.Touch\Tango.Touch.csproj">
<Project>{fd86424c-6e84-491b-8df9-3d0f5c236a2a}</Project>
<Name>Tango.Touch</Name>
@@ -351,6 +468,10 @@
<Project>{74e700b0-1156-4126-be40-ee450d3c3026}</Project>
<Name>Tango.Transport</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\Tango.WebRTC\Tango.WebRTC.csproj">
+ <Project>{09f81a12-0f77-4336-854d-9e0a74a17f9e}</Project>
+ <Name>Tango.WebRTC</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Tango.Web\Tango.Web.csproj">
<Project>{5001990f-977b-48ff-b217-0236a5022ad8}</Project>
<Name>Tango.Web</Name>
@@ -359,6 +480,10 @@
<Project>{6aa425c9-ea6a-4b01-aaed-5ff122e8b663}</Project>
<Name>Tango.WiFi</Name>
</ProjectReference>
+ <ProjectReference Include="..\Tango.PPC.Shared\Tango.PPC.Shared.csproj">
+ <Project>{208c8bd8-72c6-4e3c-acaa-351091a2acc7}</Project>
+ <Name>Tango.PPC.Shared</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.6">
@@ -375,6 +500,16 @@
<ItemGroup>
<Folder Include="Scripting\" />
</ItemGroup>
+ <ItemGroup>
+ <None Include="Resources\finger3.png" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Resources\tap.png" />
+ </ItemGroup>
+ <ItemGroup>
+ <Resource Include="Images\cl-full.png" />
+ <Resource Include="Images\lubricant2.png" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
@@ -385,7 +520,7 @@
</Target>
<ProjectExtensions>
<VisualStudio>
- <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" />
+ <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" />
</VisualStudio>
</ProjectExtensions>
</Project> \ No newline at end of file
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/ThreadLoading/IThreadLoadingService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/ThreadLoading/IThreadLoadingService.cs
new file mode 100644
index 000000000..0394a17c2
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/ThreadLoading/IThreadLoadingService.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.ThreadLoading
+{
+ public interface IThreadLoadingService
+ {
+ void StartThreadLoadingWizard();
+ void StartThreadBreakWizard();
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/AlternativeUnifiedWriteFilterManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/AlternativeUnifiedWriteFilterManager.cs
index 5fb74b2fe..ea57ca0d8 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/AlternativeUnifiedWriteFilterManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/AlternativeUnifiedWriteFilterManager.cs
@@ -4,20 +4,16 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
+using Tango.Core;
using Tango.Core.Components;
namespace Tango.PPC.Common.UWF
{
- public class AlternativeUnifiedWriteFilterManager : IUnifiedWriteFilterManager
+ public class AlternativeUnifiedWriteFilterManager : ExtendedObject, IUnifiedWriteFilterManager
{
private const string UWF_PATH = "C:\\Windows\\Sysnative\\uwfmgr.exe";
/// <summary>
- /// Gets a value indicating whether UWF if currently enabled on the system.
- /// </summary>
- public bool IsEnabled { get; private set; }
-
- /// <summary>
/// Installs and configures the service (requires restart).
/// </summary>
/// <returns></returns>
@@ -47,14 +43,15 @@ namespace Tango.PPC.Common.UWF
command.OutputEncoding = CmdCommand.OutEncoding.Unicode;
await command.Run();
- command = new CmdCommand("wmic", $"computersystem where name=\"%computername%\" set AutomaticManagedPagefile=False");
- await command.Run();
+ //We don't use page file anymore since upgrade to 4GB RAM. But keep this just in case.
+ //command = new CmdCommand("wmic", $"computersystem where name=\"%computername%\" set AutomaticManagedPagefile=False");
+ //await command.Run();
- command = new CmdCommand("wmic", $"pagefileset create name=\"p:\\pagefile.sys\"");
- await command.Run();
+ //command = new CmdCommand("wmic", $"pagefileset create name=\"p:\\pagefile.sys\"");
+ //await command.Run();
- command = new CmdCommand("wmic", $"pagefileset where name=\"p:\\\\pagefile.sys\" set InitialSize=16,MaximumSize=3870");
- await command.Run();
+ //command = new CmdCommand("wmic", $"pagefileset where name=\"p:\\\\pagefile.sys\" set InitialSize=16,MaximumSize=3870");
+ //await command.Run();
}
/// <summary>
@@ -67,5 +64,32 @@ namespace Tango.PPC.Common.UWF
command.OutputEncoding = CmdCommand.OutEncoding.Unicode;
await command.Run();
}
+
+ /// <summary>
+ /// Gets a value indicating whether UWF if currently enabled on the system.
+ /// </summary>
+ /// <returns></returns>
+ public async Task<bool> IsEnabled()
+ {
+ String pattern = @"Next Session Settings[\n\r]+FILTER SETTINGS[\n\r]+\s+Filter state:\s+(OFF|ON)";
+
+ LogManager.Log($"Getting UWF status using pattern '{pattern}'...");
+ CmdCommand command = new CmdCommand(UWF_PATH, $"get-config");
+ command.ExitStrategy = CmdCommand.ExitStrategies.StandardOutput;
+ command.OutputEncoding = CmdCommand.OutEncoding.Unicode;
+ var result = await command.Run();
+
+ Match match = Regex.Match(result.StandardOutput, pattern);
+ if (match != null && match.Success && match.Groups.Count > 1)
+ {
+ var value = match.Groups[1].Value;
+
+ LogManager.Log($"UWF pattern parsing result: '{value}'.");
+
+ return value.ToStringOrEmpty().Trim() == "ON";
+ }
+
+ return false;
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/DefaultUnifiedWriteFilterManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/DefaultUnifiedWriteFilterManager.cs
index 65cb3f466..5684f6926 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/DefaultUnifiedWriteFilterManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/DefaultUnifiedWriteFilterManager.cs
@@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
+using Tango.Core;
using Tango.Core.Components;
namespace Tango.PPC.Common.UWF
@@ -11,7 +13,7 @@ namespace Tango.PPC.Common.UWF
/// Represents the default unified writer filter manager.
/// </summary>
/// <seealso cref="Tango.PPC.Common.UWF.IUnifiedWriteFilterManager" />
- public class DefaultUnifiedWriteFilterManager : IUnifiedWriteFilterManager
+ public class DefaultUnifiedWriteFilterManager : ExtendedObject, IUnifiedWriteFilterManager
{
private const int UWF_CAPACITY_MB = 5000;
private const string UWF_PATH = "C:\\Windows\\Sysnative\\uwfmgr.exe";
@@ -24,11 +26,6 @@ namespace Tango.PPC.Common.UWF
};
/// <summary>
- /// Gets a value indicating whether UWF if currently enabled on the system.
- /// </summary>
- public bool IsEnabled { get; }
-
- /// <summary>
/// Installs and configures the service (requires restart).
/// </summary>
public async Task Setup()
@@ -95,5 +92,31 @@ namespace Tango.PPC.Common.UWF
command.OutputEncoding = CmdCommand.OutEncoding.Unicode;
await command.Run();
}
+
+ /// <summary>
+ /// Gets a value indicating whether UWF if currently enabled on the system.
+ /// </summary>
+ /// <returns></returns>
+ public async Task<bool> IsEnabled()
+ {
+ String pattern = @"Next Session Settings[\n\r]+FILTER SETTINGS[\n\r]+\s+Filter state:\s+(OFF|ON)";
+
+ LogManager.Log($"Getting UWF status using pattern '{pattern}'...");
+ CmdCommand command = new CmdCommand(UWF_PATH, $"get-config");
+ command.OutputEncoding = CmdCommand.OutEncoding.Unicode;
+ var result = await command.Run();
+
+ Match match = Regex.Match(result.StandardOutput, pattern);
+ if (match != null && match.Success && match.Groups.Count > 1)
+ {
+ var value = match.Groups[1].Value;
+
+ LogManager.Log($"UWF pattern parsing result: '{value}'.");
+
+ return value.ToStringOrEmpty().Trim() == "ON";
+ }
+
+ return false;
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/IUnifiedWriteFilterManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/IUnifiedWriteFilterManager.cs
index 05fa1876a..b6a661ab5 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/IUnifiedWriteFilterManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UWF/IUnifiedWriteFilterManager.cs
@@ -14,7 +14,8 @@ namespace Tango.PPC.Common.UWF
/// <summary>
/// Gets a value indicating whether UWF if currently enabled on the system.
/// </summary>
- bool IsEnabled { get; }
+ /// <returns></returns>
+ Task<bool> IsEnabled();
/// <summary>
/// Installs and configures the service (requires restart).
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/DefaultPackageRunner.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/DefaultPackageRunner.cs
new file mode 100644
index 000000000..68b31da4e
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/DefaultPackageRunner.cs
@@ -0,0 +1,362 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Core.Helpers;
+using Tango.PPC.Common.Application;
+using Tango.PPC.Common.Connection;
+using Tango.PPC.Common.Notifications;
+using Tango.PPC.Shared.Updates;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ public class DefaultPackageRunner : ExtendedObject, IPackageRunner
+ {
+ private JsonSerializerSettings _jsonSettings;
+ private String _configFile;
+ private PackagesFile _packagesFile;
+
+ public event EventHandler<PackageStateChangedEventArgs> PackageStateChanged;
+ public event EventHandler<PackageProgressEventArgs> PackageProgress;
+
+ public DefaultPackageRunner()
+ {
+ _jsonSettings = new JsonSerializerSettings
+ {
+ Formatting = Formatting.Indented,
+ Error = (sender, args) =>
+ {
+ args.ErrorContext.Handled = true;
+ LogManager.Log(args.ErrorContext.Error.Message);
+ }
+ };
+
+ _jsonSettings.Converters.Add(new StringEnumConverter(false));
+
+ _configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Twine", "Tango", "Packages", "packages.json");
+ }
+
+ public Task<PackagesFile> GetPackagesFile()
+ {
+ return Task.Factory.StartNew<PackagesFile>(() =>
+ {
+ if (_packagesFile != null)
+ {
+ return _packagesFile;
+ }
+ else
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(_configFile));
+
+ _packagesFile = new PackagesFile();
+
+ try
+ {
+ if (File.Exists(_configFile))
+ {
+ LogManager.Log("Loading packages config from " + _configFile + "...");
+ _packagesFile = JsonConvert.DeserializeObject<PackagesFile>(File.ReadAllText(_configFile), _jsonSettings);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error loading packages file.");
+ }
+
+ return _packagesFile;
+ }
+ });
+ }
+
+ private void SavePackagesConfig()
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(_configFile));
+
+ String json = String.Empty;
+
+ if (_packagesFile != null)
+ {
+ json = JsonConvert.SerializeObject(_packagesFile, _jsonSettings);
+ }
+ else
+ {
+ json = JsonConvert.SerializeObject(new PackagesFile(), _jsonSettings);
+ }
+
+ File.WriteAllText(_configFile, json);
+ }
+
+ public Task<PackageRunnerResult> Run(PackageType type, Version deltaVersion, String packagesFolder)
+ {
+ return Task.Factory.StartNew<PackageRunnerResult>(() =>
+ {
+ PackageRunnerResult result = new PackageRunnerResult();
+
+ PackageContext context = new PackageContext();
+ context.ApplicationManager = TangoIOC.Default.GetInstance<IPPCApplicationManager>();
+ context.MachineProvider = TangoIOC.Default.GetInstance<IMachineProvider>();
+ context.NotificationProvider = TangoIOC.Default.GetInstance<INotificationProvider>();
+ context.InstalledVersion = context.ApplicationManager.Version;
+ context.DeltaVersion = deltaVersion;
+
+ LogManager.Log($"Running {type}-update packages...");
+
+ //Get installed packages.
+ _packagesFile = GetPackagesFile().Result;
+
+ LogManager.Log($"Installed packages file:\n{_packagesFile}");
+
+ if (Debugger.IsAttached)
+ {
+ LogManager.Log("Debugger attached detected. switching packages folder to main application path...");
+
+ //TO DEBUG PACKAGES --
+ //
+ //On DEBUG build the packages assemblies are copied to the main application output folder using post-build events.
+ //Then, if a debugger is attached, we change the packages folder to the main output folder so they can be debugged easily.
+ packagesFolder = AssemblyHelper.GetCurrentAssemblyFolder();
+ }
+
+ LogManager.Log($"Scanning for packages on '{packagesFolder}'...");
+
+ //Get all packages in folder.
+ foreach (var packageFile in Directory.GetFiles(packagesFolder, "*.dll"))
+ {
+ LogManager.Log($"Loading assembly '{Path.GetFileName(packageFile)}'...");
+
+ Assembly asm;
+
+ //Load assembly and investigate for types based on package type.
+ try
+ {
+ asm = Assembly.LoadFile(packageFile);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error loading assembly!");
+ continue;
+ }
+
+ try
+ {
+ foreach (var packageType in asm.GetTypes().Where(
+ x => typeof(IPPCPackage).IsAssignableFrom(x) &&
+ x.GetCustomAttribute<PPCPackageAttribute>() != null &&
+ x.GetCustomAttribute<PPCPackageAttribute>().Type == type))
+ {
+ LogManager.Log($"Checking package '{packageType.FullName}'...");
+
+ try
+ {
+ //Getting installed package from file.
+ var installedPackage = _packagesFile.PackageInstallations.SingleOrDefault(x => x.PackageName == packageType.FullName);
+
+ //Check if requires installation.
+ if (installedPackage == null || installedPackage.State != PackageInstallationState.Installed)
+ {
+ if (installedPackage == null)
+ {
+ LogManager.Log("Package was never installed.");
+
+ installedPackage = new PackageInstallation();
+ installedPackage.State = PackageInstallationState.NotInstalled;
+ installedPackage.PackageName = packageType.FullName;
+ installedPackage.Type = type;
+ _packagesFile.PackageInstallations.Add(installedPackage);
+ }
+ else
+ {
+ LogManager.Log($"Package installation state is '{installedPackage.State}' due to {installedPackage.FailedReason}");
+ }
+
+ LogManager.Log("Installing package...");
+
+ //Install package...
+ var att = packageType.GetCustomAttribute<PPCPackageAttribute>();
+
+ var packageInstance = Activator.CreateInstance(packageType) as IPPCPackage;
+
+ if (packageInstance != null)
+ {
+ try
+ {
+ OnPackageRuns(att.Name, installedPackage.State, installedPackage.Type);
+ installedPackage.InstallationDate = DateTime.Now;
+ context.ProgressAction = (message, isIntermediate, progress, total) =>
+ {
+ PackageProgress?.Invoke(this, new PackageProgressEventArgs()
+ {
+ PackageName = att.Name,
+ Message = message,
+ IsIntermediate = isIntermediate,
+ Progress = progress,
+ Total = total
+ });
+ };
+
+ PackageProgress?.Invoke(this, new PackageProgressEventArgs()
+ {
+ PackageName = type == PackageType.Pre ? "Preparing" : "Finalizing",
+ Message = att.Name,
+ IsIntermediate = true,
+ Progress = 0,
+ Total = 100
+ });
+
+ packageInstance.Run(context).GetAwaiter().GetResult();
+ installedPackage.State = PackageInstallationState.Installed;
+ installedPackage.FailedReason = null;
+ OnPackageRuns(att.Name, installedPackage.State, installedPackage.Type);
+ LogManager.Log("Package installed successfully.");
+
+ if (att.RequiresRestart)
+ {
+ result.RestartRequired = true;
+ }
+
+ Thread.Sleep(2000);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Package installation failed.");
+ installedPackage.State = PackageInstallationState.Failed;
+ installedPackage.FailedReason = ex.FlattenMessage();
+ OnPackageRuns(att.Name, installedPackage.State, installedPackage.Type);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ LogManager.Log("Package is already installed.");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error in handling the package!");
+ continue;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error investigating assembly!");
+ continue;
+ }
+ }
+
+ //Save package file.
+ LogManager.Log("Running packages has completed. Saving packages config file.");
+
+ try
+ {
+ SavePackagesConfig();
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error saving packages file!");
+ }
+
+ return result;
+ });
+ }
+
+ public Task<bool> IsPackageInstallationRequired(PackageType type, String packagesFolder)
+ {
+ return Task.Factory.StartNew<bool>(() =>
+ {
+ LogManager.Log("Checking if any package installation is required...");
+
+ _packagesFile = GetPackagesFile().Result;
+
+ //Get all packages in folder.
+ foreach (var packageFile in Directory.GetFiles(packagesFolder, "*.dll"))
+ {
+ LogManager.Log($"Loading assembly '{Path.GetFileName(packageFile)}'...");
+
+ Assembly asm;
+
+ //Load assembly and investigate for types based on package type.
+ try
+ {
+ asm = Assembly.LoadFile(packageFile);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error loading assembly!");
+ continue;
+ }
+
+ try
+ {
+ foreach (var packageType in asm.GetTypes().Where(
+ x => typeof(IPPCPackage).IsAssignableFrom(x) &&
+ x.GetCustomAttribute<PPCPackageAttribute>() != null &&
+ x.GetCustomAttribute<PPCPackageAttribute>().Type == type))
+ {
+ LogManager.Log($"Checking package '{packageType.FullName}'...");
+
+ try
+ {
+ //Getting installed package from file.
+ var installedPackage = _packagesFile.PackageInstallations.SingleOrDefault(x => x.PackageName == packageType.FullName);
+
+ //Check if requires installation.
+ if (installedPackage == null || installedPackage.State != PackageInstallationState.Installed)
+ {
+ if (installedPackage == null)
+ {
+ LogManager.Log("Package was never installed.");
+ return true;
+ }
+ else
+ {
+ LogManager.Log($"Package installation state is '{installedPackage.State}' due to {installedPackage.FailedReason}");
+ return true;
+ }
+ }
+ else
+ {
+ LogManager.Log("Package is already installed.");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error in handling the package!");
+ continue;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error investigating assembly!");
+ continue;
+ }
+ }
+
+ LogManager.Log("No packages to install.");
+
+ return false;
+ });
+ }
+
+ protected virtual void OnPackageRuns(String packageName, PackageInstallationState state, PackageType type)
+ {
+ PackageStateChanged?.Invoke(this, new PackageStateChangedEventArgs()
+ {
+ PackageName = packageName,
+ State = state,
+ PackageType = type,
+ });
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/IPPCPackage.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/IPPCPackage.cs
new file mode 100644
index 000000000..d9dc70135
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/IPPCPackage.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ public interface IPPCPackage
+ {
+ Task Run(PackageContext context);
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/IPackageRunner.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/IPackageRunner.cs
new file mode 100644
index 000000000..cdffd1ddc
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/IPackageRunner.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.PPC.Shared.Updates;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ public interface IPackageRunner
+ {
+ event EventHandler<PackageStateChangedEventArgs> PackageStateChanged;
+ event EventHandler<PackageProgressEventArgs> PackageProgress;
+ Task<PackagesFile> GetPackagesFile();
+ Task<PackageRunnerResult> Run(PackageType type, Version deltaVersion, String packagesFolder);
+ Task<bool> IsPackageInstallationRequired(PackageType type, String packagesFolder);
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PPCPackageAttribute.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PPCPackageAttribute.cs
new file mode 100644
index 000000000..6067b0c18
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PPCPackageAttribute.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.PPC.Shared.Updates;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ [AttributeUsage(AttributeTargets.Class)]
+ public class PPCPackageAttribute : Attribute
+ {
+ public String Name { get; set; }
+
+ public PackageType Type { get; set; }
+
+ public bool RequiresRestart { get; set; }
+
+ public PPCPackageAttribute(PackageType type, String name, bool requiresRestart)
+ {
+ Type = type;
+ Name = name;
+ RequiresRestart = requiresRestart;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageContext.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageContext.cs
new file mode 100644
index 000000000..7c8736112
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageContext.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.PPC.Common.Application;
+using Tango.PPC.Common.Connection;
+using Tango.PPC.Common.Notifications;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ public class PackageContext
+ {
+ public delegate void PackageProgressDelegate(String message, bool isIntermediate = true, double progress = 0, double maximum = 100);
+
+ public IPPCApplicationManager ApplicationManager { get; set; }
+ public IMachineProvider MachineProvider { get; set; }
+ public INotificationProvider NotificationProvider { get; set; }
+ public Version InstalledVersion { get; set; }
+ public Version DeltaVersion { get; set; }
+ internal PackageProgressDelegate ProgressAction { get; set; }
+
+ public void ReportProgress(String message, bool isIntermediate = true, double progress = 0, double maximum = 100)
+ {
+ ProgressAction?.Invoke(message, isIntermediate, progress, maximum);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageProgressEventArgs.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageProgressEventArgs.cs
new file mode 100644
index 000000000..ebf0b23a6
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageProgressEventArgs.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ public class PackageProgressEventArgs : EventArgs
+ {
+ public String PackageName { get; set; }
+ public String Message { get; set; }
+ public bool IsIntermediate { get; set; }
+ public double Progress { get; set; }
+ public double Total { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageRunnerResult.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageRunnerResult.cs
new file mode 100644
index 000000000..55f3a490a
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageRunnerResult.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ public class PackageRunnerResult
+ {
+ public bool RestartRequired { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageStateChangedEventArgs.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageStateChangedEventArgs.cs
new file mode 100644
index 000000000..cadba4c72
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackageStateChangedEventArgs.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.PPC.Shared.Updates;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ public class PackageStateChangedEventArgs : EventArgs
+ {
+ public PackageInstallationState State { get; set; }
+ public String PackageName { get; set; }
+ public PackageType PackageType { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackagesFile.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackagesFile.cs
new file mode 100644
index 000000000..7af30d2ec
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/UpdatePackages/PackagesFile.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.PPC.Shared.Updates;
+
+namespace Tango.PPC.Common.UpdatePackages
+{
+ public class PackagesFile
+ {
+ public List<PackageInstallation> PackageInstallations { get; set; }
+
+ public PackagesFile()
+ {
+ PackageInstallations = new List<PackageInstallation>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateRequest.cs
index b98848e4f..8e13ea7c4 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateRequest.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateRequest.cs
@@ -9,7 +9,21 @@ namespace Tango.PPC.Common.Web
{
public class CheckForUpdateRequest : WebRequestMessage
{
- public String SerialNumber { get; set; }
public String Version { get; set; }
+ public String FirmwareVersion { get; set; }
+
+ public List<UpdatedEntity> Rmls { get; set; }
+ public List<UpdatedEntity> HardwareVersions { get; set; }
+ public List<UpdatedEntity> Catalogs { get; set; }
+ public DateTime MachineLastUpdated { get; set; }
+ public List<String> UsedRmlsGuids { get; set; }
+
+ public CheckForUpdateRequest()
+ {
+ Rmls = new List<UpdatedEntity>();
+ HardwareVersions = new List<UpdatedEntity>();
+ Catalogs = new List<UpdatedEntity>();
+ UsedRmlsGuids = new List<string>();
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateResponse.cs
index 370c0f5ea..2fb33ebdc 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateResponse.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/CheckForUpdateResponse.cs
@@ -10,8 +10,18 @@ namespace Tango.PPC.Common.Web
public class CheckForUpdateResponse : WebResponseMessage
{
public bool IsUpdateAvailable { get; set; }
+ public bool IsDatabaseUpdateAvailable { get; set; }
public String Version { get; set; }
+ public String FirmwareVersion { get; set; }
public bool SetupFirmware { get; set; }
public bool SetupFPGA { get; set; }
+ public UpdateDBResponse UpdateDBResponse { get; set; }
+ public List<String> UsedNotExistingRmlsGuids { get; set; }
+
+ public CheckForUpdateResponse()
+ {
+ UpdateDBResponse = new UpdateDBResponse();
+ UsedNotExistingRmlsGuids = new List<string>();
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataRequest.cs
new file mode 100644
index 000000000..bbb0e883b
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataRequest.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.DTO;
+using Tango.Transport.Web;
+
+namespace Tango.PPC.Common.Web
+{
+ public class DownloadMachineDataRequest : WebRequestMessage
+ {
+ public bool RequestJobs { get; set; }
+ public bool RequestJobRuns { get; set; }
+ public bool RequestMachineEvents { get; set; }
+ public bool RequestDataStoreItems { get; set; }
+
+ public int MaxJobs { get; set; }
+ public int MaxJobRuns { get; set; }
+ public int MaxMachinesEvents { get; set; }
+ public int MaxDataStoreItems { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataResponse.cs
new file mode 100644
index 000000000..e90c7c2ac
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataResponse.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.DTO;
+using Tango.Transport.Web;
+
+namespace Tango.PPC.Common.Web
+{
+ public class DownloadMachineDataResponse : WebResponseMessage
+ {
+ public List<JobDTO> Jobs { get; set; }
+ public List<JobRunDTO> JobRuns { get; set; }
+ public List<MachinesEventDTO> MachineEvents { get; set; }
+ public List<DataStoreItemDTO> DataStoreItems { get; set; }
+
+ public DownloadMachineDataResponse()
+ {
+ Jobs = new List<JobDTO>();
+ JobRuns = new List<JobRunDTO>();
+ MachineEvents = new List<MachinesEventDTO>();
+ DataStoreItems = new List<DataStoreItemDTO>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateRequest.cs
index a32d3d497..db4080dff 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateRequest.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateRequest.cs
@@ -9,6 +9,6 @@ namespace Tango.PPC.Common.Web
{
public class DownloadUpdateRequest : WebRequestMessage
{
- public String SerialNumber { get; set; }
+
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateResponse.cs
index 3b09c1525..2fc7e4810 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateResponse.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadUpdateResponse.cs
@@ -10,10 +10,16 @@ namespace Tango.PPC.Common.Web
{
public class DownloadUpdateResponse : WebResponseMessage
{
+ public String NotifyCompletedToken { get; set; }
+
public String Version { get; set; }
+ public String FirmwareVersion { get; set; }
+
public String BlobAddress { get; set; }
+ public String CdnAddress { get; set; }
+
public DataSource DataSource { get; set; }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LatestVersionResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LatestVersionResponse.cs
index d2ed08f7d..eb5ef7f5a 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LatestVersionResponse.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LatestVersionResponse.cs
@@ -11,5 +11,6 @@ namespace Tango.PPC.Common.Web
public class LatestVersionResponse : WebResponseMessage
{
public String Version { get; set; }
+ public String FirmwareVersion { get; set; }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LoginRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LoginRequest.cs
index f8588f6b0..9ae0d65ae 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LoginRequest.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/LoginRequest.cs
@@ -11,6 +11,7 @@ namespace Tango.PPC.Common.Web
{
public LoginMode Mode { get; set; }
public String SerialNumber { get; set; }
+ public String MachineGuid { get; set; }
public String Email { get; set; }
public String Password { get; set; }
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupRequest.cs
index 821828a48..a9e68df36 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupRequest.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupRequest.cs
@@ -9,7 +9,6 @@ namespace Tango.PPC.Common.Web
{
public class MachineSetupRequest : WebRequestMessage
{
- public String SerialNumber { get; set; }
public String DeviceID { get; set; }
public String DeviceName { get; set; }
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupResponse.cs
index b5a4c425d..f5d03c6ce 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupResponse.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineSetupResponse.cs
@@ -10,10 +10,16 @@ namespace Tango.PPC.Common.Web
{
public class MachineSetupResponse : WebResponseMessage
{
+ public String NotifyCompletedToken { get; set; }
+
public String Version { get; set; }
+ public String FirmwareVersion { get; set; }
+
public String BlobAddress { get; set; }
+ public String CdnAddress { get; set; }
+
public DataSource DataSource { get; set; }
public String OSKey { get; set; }
@@ -25,5 +31,6 @@ namespace Tango.PPC.Common.Web
public bool SetupFPGA { get; set; }
public bool IsDemo { get; set; }
public String DeviceComPort { get; set; }
+ public String Organization { get; set; }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineUpdateCompletedRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineUpdateCompletedRequest.cs
new file mode 100644
index 000000000..dffe1f15b
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineUpdateCompletedRequest.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.Enumerations;
+using Tango.Transport.Web;
+
+namespace Tango.PPC.Common.Web
+{
+ public class MachineUpdateCompletedRequest : WebRequestMessage
+ {
+ public String Token { get; set; }
+ public TangoUpdateStatuses Status { get; set; }
+ public String FailedReason { get; set; }
+ public String FailedLog { get; set; }
+ public bool ReportsAboutDbCheckNoDifferences { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineUpdateCompletedResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineUpdateCompletedResponse.cs
new file mode 100644
index 000000000..72517d108
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/MachineUpdateCompletedResponse.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Transport.Web;
+
+namespace Tango.PPC.Common.Web
+{
+ public class MachineUpdateCompletedResponse : WebResponseMessage
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedRequest.cs
new file mode 100644
index 000000000..fda7a4666
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedRequest.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.DTO;
+using Tango.Transport.Web;
+
+namespace Tango.PPC.Common.Web
+{
+ public class NotifyMachineDataDownloadCompletedRequest : WebRequestMessage
+ {
+ public List<String> SynchronizedJobs { get; set; }
+ public List<String> SynchronizedJobRuns { get; set; }
+ public List<String> SynchronizedMachineEvents { get; set; }
+ public List<String> SynchronizedDataStoreItems { get; set; }
+
+ public NotifyMachineDataDownloadCompletedRequest()
+ {
+ SynchronizedJobs = new List<string>();
+ SynchronizedJobRuns = new List<string>();
+ SynchronizedMachineEvents = new List<string>();
+ SynchronizedDataStoreItems = new List<string>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedResponse.cs
new file mode 100644
index 000000000..6d5769885
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedResponse.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.DTO;
+using Tango.Transport.Web;
+
+namespace Tango.PPC.Common.Web
+{
+ public class NotifyMachineDataDownloadCompletedResponse : WebResponseMessage
+ {
+
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs
index 52c9fdef3..318512fbb 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs
@@ -30,5 +30,10 @@ namespace Tango.PPC.Common.Web
public PPCWebClient(string address, string token) : base(address, token)
{
}
+
+ public PPCWebClient(PPCWebClient other, TimeSpan requestTimeout) : base(other)
+ {
+ RequestTimeout = requestTimeout;
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs
index 2df343241..ff972acb2 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs
@@ -41,6 +41,15 @@ namespace Tango.PPC.Common.Web
}
/// <summary>
+ /// Initializes a new instance of the <see cref="PPCWebClientBase"/> class.
+ /// </summary>
+ /// <param name="cloned">Other instance.</param>
+ public PPCWebClientBase(PPCWebClientBase cloned) : base(cloned)
+ {
+
+ }
+
+ /// <summary>
/// Executes the MachineSetup action and returns Tango.PPC.Common.Web.MachineSetupResponse.
/// </summary>
/// <returns></returns>
@@ -59,6 +68,15 @@ namespace Tango.PPC.Common.Web
}
/// <summary>
+ /// Executes the NotifyUpdateCompleted action and returns Tango.PPC.Common.Web.MachineUpdateCompletedResponse.
+ /// </summary>
+ /// <returns></returns>
+ public Task<Tango.PPC.Common.Web.MachineUpdateCompletedResponse> NotifyUpdateCompleted(Tango.PPC.Common.Web.MachineUpdateCompletedRequest request)
+ {
+ return Post<Tango.PPC.Common.Web.MachineUpdateCompletedRequest, Tango.PPC.Common.Web.MachineUpdateCompletedResponse>("NotifyUpdateCompleted", request);
+ }
+
+ /// <summary>
/// Executes the CheckForUpdates action and returns Tango.PPC.Common.Web.CheckForUpdateResponse.
/// </summary>
/// <returns></returns>
@@ -77,6 +95,33 @@ namespace Tango.PPC.Common.Web
}
/// <summary>
+ /// Executes the UploadMachineData action and returns Tango.PPC.Common.Web.UploadMachineDataResponse.
+ /// </summary>
+ /// <returns></returns>
+ public Task<Tango.PPC.Common.Web.UploadMachineDataResponse> UploadMachineData(Tango.PPC.Common.Web.UploadMachineDataRequest request)
+ {
+ return Post<Tango.PPC.Common.Web.UploadMachineDataRequest, Tango.PPC.Common.Web.UploadMachineDataResponse>("UploadMachineData", request);
+ }
+
+ /// <summary>
+ /// Executes the DownloadMachineData action and returns Tango.PPC.Common.Web.DownloadMachineDataResponse.
+ /// </summary>
+ /// <returns></returns>
+ public Task<Tango.PPC.Common.Web.DownloadMachineDataResponse> DownloadMachineData(Tango.PPC.Common.Web.DownloadMachineDataRequest request)
+ {
+ return Post<Tango.PPC.Common.Web.DownloadMachineDataRequest, Tango.PPC.Common.Web.DownloadMachineDataResponse>("DownloadMachineData", request);
+ }
+
+ /// <summary>
+ /// Executes the NotifyMachineDataDownloadCompleted action and returns Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedResponse.
+ /// </summary>
+ /// <returns></returns>
+ public Task<Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedResponse> NotifyMachineDataDownloadCompleted(Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedRequest request)
+ {
+ return Post<Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedRequest, Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedResponse>("NotifyMachineDataDownloadCompleted", request);
+ }
+
+ /// <summary>
/// Executes the GetLatestVersion action and returns Tango.PPC.Common.Web.LatestVersionResponse.
/// </summary>
/// <returns></returns>
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/SynchronizationFailedEntity.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/SynchronizationFailedEntity.cs
new file mode 100644
index 000000000..c50044cbe
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/SynchronizationFailedEntity.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.PPC.Common.Web
+{
+ public class SynchronizationFailedEntity
+ {
+ public String Guid { get; set; }
+ public String Reason { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs
index f3b4ccb34..4d8433a56 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs
@@ -9,6 +9,7 @@ namespace Tango.PPC.Common.Web
{
public class UpdateDBRequest : WebRequestMessage
{
- public String SerialNumber { get; set; }
+ public String ApplicationVersion { get; set; }
+ public String FirmwareVersion { get; set; }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBResponse.cs
index be7c0d076..179cb7934 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBResponse.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBResponse.cs
@@ -10,6 +10,7 @@ namespace Tango.PPC.Common.Web
{
public class UpdateDBResponse : WebResponseMessage
{
+ public String NotifyCompletedToken { get; set; }
public DataSource DataSource { get; set; }
public bool PerformSchemaUpdate { get; set; }
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdatedEntity.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdatedEntity.cs
new file mode 100644
index 000000000..faee20678
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdatedEntity.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL;
+
+namespace Tango.PPC.Common.Web
+{
+ public class UpdatedEntity
+ {
+ public UpdatedEntity()
+ {
+
+ }
+
+ public UpdatedEntity(IObservableEntity entity) : this()
+ {
+ Guid = entity.Guid;
+ LastUpdated = entity.LastUpdated;
+ }
+
+ public String Guid { get; set; }
+ public DateTime LastUpdated { get; set; }
+
+ public override string ToString()
+ {
+ return $"{Guid} | {LastUpdated}";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataRequest.cs
new file mode 100644
index 000000000..8eee667cd
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataRequest.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.DTO;
+using Tango.Transport.Web;
+
+namespace Tango.PPC.Common.Web
+{
+ public class UploadMachineDataRequest : WebRequestMessage
+ {
+ public List<JobDTO> Jobs { get; set; }
+ public List<JobRunDTO> JobRuns { get; set; }
+ public List<MachinesEventDTO> MachineEvents { get; set; }
+ public List<TangoUpdateDTO> OfflineUpdates { get; set; }
+ public List<DataStoreItemDTO> DataStoreItems { get; set; }
+ public String ApplicationVersion { get; set; }
+ public String FirmwareVersion { get; set; }
+
+ public UploadMachineDataRequest()
+ {
+ Jobs = new List<JobDTO>();
+ JobRuns = new List<JobRunDTO>();
+ MachineEvents = new List<MachinesEventDTO>();
+ OfflineUpdates = new List<TangoUpdateDTO>();
+ DataStoreItems = new List<DataStoreItemDTO>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataResponse.cs
new file mode 100644
index 000000000..ba0b4089b
--- /dev/null
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataResponse.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.BL.DTO;
+using Tango.Transport.Web;
+
+namespace Tango.PPC.Common.Web
+{
+ public class UploadMachineDataResponse : WebResponseMessage
+ {
+ public List<SynchronizationFailedEntity> FailedJobs { get; set; }
+ public List<SynchronizationFailedEntity> FailedJobRuns { get; set; }
+ public List<SynchronizationFailedEntity> FailedMachineEvents { get; set; }
+ public List<SynchronizationFailedEntity> FailedOfflineUpdates { get; set; }
+ public List<SynchronizationFailedEntity> FailedDataStoreItems { get; set; }
+
+ public String NotifyCompletedToken { get; set; }
+
+ public UploadMachineDataResponse()
+ {
+ FailedJobs = new List<SynchronizationFailedEntity>();
+ FailedJobRuns = new List<SynchronizationFailedEntity>();
+ FailedMachineEvents = new List<SynchronizationFailedEntity>();
+ FailedOfflineUpdates = new List<SynchronizationFailedEntity>();
+ FailedDataStoreItems = new List<SynchronizationFailedEntity>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/packages.config b/Software/Visual_Studio/PPC/Tango.PPC.Common/packages.config
index 50785f217..adc33d349 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/packages.config
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/packages.config
@@ -6,6 +6,7 @@
<package id="FontAwesome.WPF" version="4.7.0.9" targetFramework="net46" />
<package id="Google.Protobuf" version="3.4.1" targetFramework="net46" />
<package id="Ionic.Zip" version="1.9.1.8" targetFramework="net461" />
+ <package id="Microsoft.WindowsAPICodePack-Core" version="1.1.0.0" targetFramework="net461" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
<package id="System.Data.SQLite" version="1.0.108.0" targetFramework="net46" />
<package id="System.Data.SQLite.Core" version="1.0.108.0" targetFramework="net46" />