From c7fa53084c674586ceee773ccbdc6b4c0a2ec7d4 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Mon, 9 Dec 2019 16:19:41 +0200 Subject: Implemented full TUP package update !!! --- .../ViewModels/MainViewVM.cs | 8 ++ .../ViewModels/TupViewVM.cs | 129 +++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels') diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs index 820950290..768f93de1 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs @@ -140,6 +140,12 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels set { _machineUpdatesViewVM = value; RaisePropertyChangedAuto(); } } + private TupViewVM _tupViewVM; + public TupViewVM TupViewVM + { + get { return _tupViewVM; } + set { _tupViewVM = value; RaisePropertyChangedAuto(); } + } #endregion @@ -233,6 +239,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels ResetDeviceRegistrationCommand = new RelayCommand(ResetDeviceRegistration); MachineUpdatesViewVM = new MachineUpdatesViewVM(_notification); + TupViewVM = new TupViewVM(_notification); } #endregion @@ -478,6 +485,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels HardwareConfigurationViewVM.Init(ActiveMachine.Configuration); await MachineUpdatesViewVM.Init(ActiveMachine, ActiveMachineAdapter.Context); + TupViewVM.Init(ActiveMachine); ActiveMachine.Configuration.HardwareVersionChanged += Configuration_HardwareVersionChanged; diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs new file mode 100644 index 000000000..12ed09c75 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs @@ -0,0 +1,129 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.BL.Entities; +using Tango.Core.Commands; +using Tango.MachineStudio.Common.Notifications; +using Tango.MachineStudio.Common.Tup; +using Tango.SharedUI; + +namespace Tango.MachineStudio.MachineDesigner.ViewModels +{ + public class TupViewVM : ViewModel + { + private INotificationProvider _notification; + + private String _latestVersion; + public String LatestVersion + { + get { return _latestVersion; } + set { _latestVersion = value; RaisePropertyChangedAuto(); } + } + + private Machine _machine; + public Machine Machine + { + get { return _machine; } + set { _machine = value; RaisePropertyChangedAuto(); } + } + + private String _filePath; + public String FilePath + { + get { return _filePath; } + set { _filePath = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private TupFileBuilderProgressEventArgs _progress; + public TupFileBuilderProgressEventArgs Progress + { + get { return _progress; } + set { _progress = value; RaisePropertyChangedAuto(); } + } + + public RelayCommand CreateTupFileCommand { get; set; } + + public RelayCommand SelectFileCommand { get; set; } + + public TupViewVM() + { + + } + + public TupViewVM(INotificationProvider notification) : this() + { + _notification = notification; + CreateTupFileCommand = new RelayCommand(CreateTupFile, () => FilePath != null && IsFree); + SelectFileCommand = new RelayCommand(SelectFile); + } + + public void Init(Machine machine) + { + Machine = machine; + DisplayLatestPPCVersion(); + } + + private async void DisplayLatestPPCVersion() + { + TupFileBuilder builder = new TupFileBuilder(); + + try + { + LatestVersion = await builder.GetLatestPPCVersion(Machine.SerialNumber); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error retrieving latest PPC version."); + await Task.Delay(5000); + DisplayLatestPPCVersion(); + } + } + + private void SelectFile() + { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.Title = "Select package location"; + dlg.Filter = "Tango Update Package Files|*.tup"; + dlg.DefaultExt = ".tup"; + dlg.FileName = LatestVersion == null ? $"{Machine.SerialNumber}_Update.tup" : $"{Machine.SerialNumber}_Update_v{LatestVersion}.tup"; + + if (dlg.ShowDialog().Value) + { + FilePath = dlg.FileName; + } + } + + private async void CreateTupFile() + { + try + { + LogManager.Log($"Generating TUP file to '{FilePath}'..."); + + IsFree = false; + TupFileBuilder builder = new TupFileBuilder(); + builder.Progress += Builder_Progress; + await builder.Build(Machine.SerialNumber, FilePath); + + LogManager.Log("TUP file generated successfully."); + _notification.ShowInfo("Tango update package created successfuly."); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error generating tup file."); + _notification.ShowError($"An error occurred while generating the .tup file.\n{ex.FlattenMessage()}"); + } + finally + { + IsFree = true; + } + } + + private void Builder_Progress(object sender, TupFileBuilderProgressEventArgs e) + { + Progress = e; + } + } +} -- cgit v1.3.1 From d5827e26ff5ee1b0532530bce4da3533f71a63dd Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Mon, 9 Dec 2019 20:07:12 +0200 Subject: Added LocalDB to machine prerequisit to machine studio installer. Implemented machine studio installer feature selection. Removed roslyn folder from MS release output. Added date to tup file generation. Added LocalDB start before connect service on TupFileBuilder. --- .../Machine Studio Installer.aip | 1412 +++++++++++++------- .../ViewModels/TupViewVM.cs | 2 +- .../Tup/TupFileBuilder.cs | 5 +- .../Tango.MachineStudio.UI.csproj | 3 +- 4 files changed, 914 insertions(+), 508 deletions(-) (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels') diff --git a/Software/Visual_Studio/Advanced Installer Projects/Machine Studio Installer.aip b/Software/Visual_Studio/Advanced Installer Projects/Machine Studio Installer.aip index b29828743..bfc3229b5 100644 --- a/Software/Visual_Studio/Advanced Installer Projects/Machine Studio Installer.aip +++ b/Software/Visual_Studio/Advanced Installer Projects/Machine Studio Installer.aip @@ -1,5 +1,5 @@ - + @@ -11,6 +11,7 @@ + @@ -36,11 +37,13 @@ + + + - @@ -49,26 +52,30 @@ + - + + - + - - + + + - - + + + @@ -76,10 +83,10 @@ - + - - + + @@ -89,34 +96,46 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - + + + + - + + + + + + + + + + - - + + + + @@ -129,8 +148,8 @@ - - + + @@ -157,7 +176,7 @@ - + @@ -179,7 +198,7 @@ - + @@ -205,7 +224,7 @@ - + @@ -219,7 +238,7 @@ - + @@ -228,76 +247,83 @@ - + - - - - - - - - + + + + + + + + - + - - - + + + - + - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - + + + + + + + + - - + + + - + - - + + - + - + @@ -306,9 +332,9 @@ - + - + @@ -333,429 +359,448 @@ - - - - + + + + - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -772,6 +817,7 @@ + @@ -793,8 +839,13 @@ + + + + + @@ -808,10 +859,23 @@ + + + + + + + + + + + + + - + - + @@ -834,10 +898,15 @@ + + + + + - + @@ -847,6 +916,7 @@ + @@ -855,14 +925,18 @@ + + + + @@ -870,17 +944,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -899,6 +1292,7 @@ + @@ -910,6 +1304,11 @@ + + + + + @@ -927,6 +1326,7 @@ + @@ -934,8 +1334,8 @@ - - + + @@ -947,13 +1347,15 @@ - + + + - + diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs index 12ed09c75..5d1703dc3 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/TupViewVM.cs @@ -88,7 +88,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels dlg.Title = "Select package location"; dlg.Filter = "Tango Update Package Files|*.tup"; dlg.DefaultExt = ".tup"; - dlg.FileName = LatestVersion == null ? $"{Machine.SerialNumber}_Update.tup" : $"{Machine.SerialNumber}_Update_v{LatestVersion}.tup"; + dlg.FileName = LatestVersion == null ? $"{Machine.SerialNumber}_Update_{DateTime.Now.Date.ToFileName()}.tup" : $"{Machine.SerialNumber}_Update_{DateTime.Now.Date.ToFileName()}_v{LatestVersion}.tup"; if (dlg.ShowDialog().Value) { diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilder.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilder.cs index 6dd8b82f7..fad6ce949 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilder.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tup/TupFileBuilder.cs @@ -69,9 +69,12 @@ namespace Tango.MachineStudio.Common.Tup { LogManager.Log(ex, "Could not connect using SQLEXPRESS. Trying local DB..."); - CmdCommand command = new CmdCommand("sqllocaldb", "info \"MSSQLLocalDB\""); + CmdCommand command = new CmdCommand("sqllocaldb", "start \"MSSQLLocalDB\""); var result = command.Run().Result; + command = new CmdCommand("sqllocaldb", "info \"MSSQLLocalDB\""); + result = command.Run().Result; + String pattern = "np:.+"; Regex reg = new Regex(pattern); var match = reg.Match(result.StandardOutput); diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj index c6cf624b3..470cfe4c7 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj @@ -654,7 +654,8 @@ copy /Y "$(SolutionDir)Referenced Assemblies\vcruntime140d.dll" "$(TargetDir)" copy /Y "$(SolutionDir)Referenced Assemblies\Microsoft.WITDataStore32.dll" "$(TargetDir)" if $(ConfigurationName) == Release del *.xml -if $(ConfigurationName) == Release RD /S /Q "$(TargetDir)ProtoCompilers\" +if $(ConfigurationName) == Release RD /S /Q "$(TargetDir)ProtoCompilers\" +if $(ConfigurationName) == Release RD /S /Q "$(TargetDir)Roslyn\" -- cgit v1.3.1 From 621230afe9d9040536b43241e63117c9bb34beaa Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Wed, 11 Dec 2019 20:57:30 +0200 Subject: Implemented Jobs, JobRuns & Machine Events Synchronization. Added synchronizations to Updates view on Machine Designer. Removed request response events logging from machine studio. Fixed issue with exception throwing from machine service. Implemented "New jobs synchronized" notification item to PPC. Added synchronization to PPC settings. Added Synchronization view to technician module. Implemented PPC Schema synchronizer utility. Added custom query support to EntityCollectionBuilder. Added synchronization status to TangoUpdate. Removed FK from Jobs and Job runs. --- Software/DB/PPC/Tango.mdf | Bin 75497472 -> 75497472 bytes Software/DB/PPC/Tango_log.ldf | Bin 53673984 -> 53673984 bytes Software/DB/Tango.mdf | Bin 75497472 -> 75497472 bytes Software/DB/Tango_log.ldf | Bin 22675456 -> 22675456 bytes .../ViewModels/MainViewVM.cs | 1 + .../ViewModels/MachineUpdatesViewVM.cs | 48 +- .../Views/MachineUpdateDetailsDialog.xaml | 3 + .../Views/MachineUpdatesView.xaml | 22 +- .../EventLogging/DefaultEventLogger.cs | 6 +- .../Web/MachineStudioWebClientBase.cs | 9 + .../Tango.MachineStudio.UI/App.xaml.cs | 2 +- .../DefaultAuthenticationProvider.cs | 5 +- .../Tango.MachineStudio.UI/ViewModelLocator.cs | 14 +- .../PPC/Modules/Tango.PPC.Jobs/Images/sync-job.png | Bin 0 -> 6743 bytes .../PPC/Modules/Tango.PPC.Jobs/Images/sync.png | Bin 0 -> 4127 bytes .../NewSynchronizardJobsNotificationItem.cs | 32 ++ .../NewSynchronizardJobsNotificationItemView.xaml | 26 + ...ewSynchronizardJobsNotificationItemView.xaml.cs | 30 ++ .../Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj | 13 +- .../Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs | 1 + .../Tango.PPC.Jobs/ViewModels/JobsViewVM.cs | 25 +- .../ViewModels/MainViewVM.cs | 59 +++ .../Tango.PPC.MachineSettings/Views/MainView.xaml | 43 +- .../Modules/Tango.PPC.Technician/Images/sync.png | Bin 0 -> 4127 bytes .../Tango.PPC.Technician.csproj | 11 + .../Tango.PPC.Technician/ViewModelLocator.cs | 12 + .../ViewModels/SynchronizationViewVM.cs | 41 ++ .../ViewModels/SystemViewVM.cs | 6 +- .../Tango.PPC.Technician/Views/CatalogView.xaml | 20 +- .../Tango.PPC.Technician/Views/MainView.xaml | 1 + .../Views/SynchronizationView.xaml | 133 +++++ .../Views/SynchronizationView.xaml.cs | 28 ++ .../Application/IPPCApplicationManager.cs | 5 + .../Connection/DefaultMachineProvider.cs | 18 + .../MachineSetup/MachineSetupManager.cs | 12 +- .../MachineUpdate/MachineUpdateManager.cs | 2 + .../PPC/Tango.PPC.Common/PPCSettings.cs | 24 + .../PPC/Tango.PPC.Common/PPCViewModel.cs | 7 + .../DefaultMachineDataSynchronizer.cs | 554 +++++++++++++++++++++ .../Synchronization/IMachineDataSynchronizer.cs | 26 + .../SynchronizationEndedEventArgs.cs | 15 + .../Synchronization/SynchronizationState.cs | 23 + .../Synchronization/SynchronizationStatus.cs | 47 ++ .../SynchronizationStatusChangedEventArgs.cs | 13 + .../PPC/Tango.PPC.Common/Tango.PPC.Common.csproj | 15 +- .../Web/DownloadMachineDataRequest.cs | 22 + .../Web/DownloadMachineDataResponse.cs | 25 + .../NotifyMachineDataDownloadCompletedRequest.cs | 25 + .../NotifyMachineDataDownloadCompletedResponse.cs | 16 + .../PPC/Tango.PPC.Common/Web/PPCWebClient.cs | 5 + .../PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs | 36 ++ .../Web/SynchronizationFailedEntity.cs | 14 + .../PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs | 2 + .../Web/UploadMachineDataRequest.cs | 27 + .../Web/UploadMachineDataResponse.cs | 26 + .../Tango.PPC.SchemaSynchronizer.CLI/App.config | 6 + .../Tango.PPC.SchemaSynchronizer.CLI/Program.cs | 54 ++ .../Properties/AssemblyInfo.cs | 36 ++ .../Tango.PPC.SchemaSynchronizer.CLI.csproj | 63 +++ .../PPCApplication/DefaultPPCApplicationManager.cs | 21 +- .../PPC/Tango.PPC.UI/ViewModelLocator.cs | 14 +- .../Builders/EntityCollectionBuilderBase.cs | 38 ++ Software/Visual_Studio/Tango.BL/DTO/JobDTO.cs | 5 + Software/Visual_Studio/Tango.BL/DTO/JobDTOBase.cs | 8 + .../Visual_Studio/Tango.BL/DTO/JobRunDTOBase.cs | 8 + .../Tango.BL/DTO/MachinesEventDTOBase.cs | 8 + Software/Visual_Studio/Tango.BL/DTO/SegmentDTO.cs | 5 + Software/Visual_Studio/Tango.BL/Entities/Job.cs | 1 + .../Visual_Studio/Tango.BL/Entities/JobBase.cs | 38 ++ .../Visual_Studio/Tango.BL/Entities/JobRunBase.cs | 38 ++ .../Tango.BL/Entities/MachinesEventBase.cs | 38 ++ .../Visual_Studio/Tango.BL/Entities/TangoUpdate.cs | 22 +- .../Tango.BL/Enumerations/TangoUpdateStatuses.cs | 9 +- .../Visual_Studio/Tango.BL/IObservableEntity.cs | 5 + .../Visual_Studio/Tango.BL/ObservableEntity.cs | 12 + .../Tango.BL/ObservablesContextExtension.cs | 31 +- .../Templates/TangoWebClientCodeFile.cshtml | 9 + .../ExtensionMethods/ExceptionExtensions.cs | 17 + Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs | 1 + .../Visual_Studio/Tango.DAL.Remote/DB/JOB_RUNS.cs | 1 + .../Tango.DAL.Remote/DB/MACHINES_EVENTS.cs | 1 + .../Tango.DAL.Remote/DB/RemoteADO.edmx | 9 + .../Tango.DAL.Remote/DB/RemoteADO.edmx.diagram | 146 +++--- .../Tango.Settings/SettingsManager.cs | 1 - .../Tango.Transport/Web/WebTransportClient.cs | 7 + .../Tango.Web/Controllers/TangoController.cs | 2 +- Software/Visual_Studio/Tango.Web/TangoWebClient.cs | 28 +- Software/Visual_Studio/Tango.sln | 43 ++ .../Controllers/MachineStudioController.cs | 11 +- .../Controllers/PPCController.cs | 277 ++++++++++- .../Web/Tango.MachineService/Web.config | 7 +- 91 files changed, 2444 insertions(+), 126 deletions(-) create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync-job.png create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync.png create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItem.cs create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItemView.xaml create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItemView.xaml.cs create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Images/sync.png create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModels/SynchronizationViewVM.cs create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/SynchronizationView.xaml create mode 100644 Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/SynchronizationView.xaml.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/DefaultMachineDataSynchronizer.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/IMachineDataSynchronizer.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationEndedEventArgs.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationState.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatus.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatusChangedEventArgs.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataRequest.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataResponse.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedRequest.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedResponse.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Web/SynchronizationFailedEntity.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataRequest.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataResponse.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/App.config create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Program.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Properties/AssemblyInfo.cs create mode 100644 Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Tango.PPC.SchemaSynchronizer.CLI.csproj (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels') diff --git a/Software/DB/PPC/Tango.mdf b/Software/DB/PPC/Tango.mdf index 92884377a..41f7704e8 100644 Binary files a/Software/DB/PPC/Tango.mdf and b/Software/DB/PPC/Tango.mdf differ diff --git a/Software/DB/PPC/Tango_log.ldf b/Software/DB/PPC/Tango_log.ldf index 9a2dcca84..efb976613 100644 Binary files a/Software/DB/PPC/Tango_log.ldf and b/Software/DB/PPC/Tango_log.ldf differ diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf index 0336edc76..596d62f57 100644 Binary files a/Software/DB/Tango.mdf and b/Software/DB/Tango.mdf differ diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf index dd9945ab8..ecf74c79f 100644 Binary files a/Software/DB/Tango_log.ldf and b/Software/DB/Tango_log.ldf differ diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/ViewModels/MainViewVM.cs index 63cfa10b8..46b4c301f 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Developer/ViewModels/MainViewVM.cs @@ -1914,6 +1914,7 @@ namespace Tango.MachineStudio.Developer.ViewModels { LogManager.Log(String.Format("Saving the active job {0}...", ActiveJob.Name)); ActiveJob.LastUpdated = DateTime.UtcNow; + ActiveJob.IsSynchronized = false; ActiveJob.Rml = SelectedRML; ActiveJob.EstimatedDurationMili = (int)EstimatedDuration.TotalMilliseconds; _activeJobDbContext.SaveChanges(); diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MachineUpdatesViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MachineUpdatesViewVM.cs index 7ff64c505..3ac3110bb 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MachineUpdatesViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MachineUpdatesViewVM.cs @@ -8,6 +8,7 @@ using System.Windows.Data; using Tango.BL; using Tango.BL.Builders; using Tango.BL.Entities; +using Tango.Core.Commands; using Tango.MachineStudio.Common.Notifications; using Tango.MachineStudio.MachineDesigner.Views; using Tango.SharedUI; @@ -17,6 +18,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels public class MachineUpdatesViewVM : ViewModel { private INotificationProvider _notification; + private ObservablesContext _context; #region Properties @@ -70,6 +72,19 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels set { _displayDatabaseUpdates = value; RaisePropertyChangedAuto(); OnFilterChanged(); } } + private bool _displaySynchronizations; + public bool DisplaySynchronizations + { + get { return _displaySynchronizations; } + set { _displaySynchronizations = value; RaisePropertyChangedAuto(); OnFilterChanged(); } + } + + #endregion + + #region Commands + + public RelayCommand RefreshCommand { get; set; } + #endregion #region Constructors @@ -79,6 +94,9 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels DisplayApplicationUpdates = true; DisplayMachineSetups = true; DisplayDatabaseUpdates = true; + DisplaySynchronizations = true; + + RefreshCommand = new RelayCommand(Refresh, () => IsFree); } public MachineUpdatesViewVM(INotificationProvider notificationProvider) : this() @@ -92,17 +110,36 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels public async Task Init(Machine machine, ObservablesContext context) { - Machine = machine; - Updates = (await new TangoUpdatesCollectionBuilder(context).Set(x => x.MachineGuid == machine.Guid).BuildAsync()).OrderByDescending(x => x.StartDate).ToList(); - UpdatesView = CollectionViewSource.GetDefaultView(Updates); - UpdatesView.Filter = UpdatesFilter; - OnFilterChanged(); + try + { + _context = context; + Machine = machine; + Updates = (await new TangoUpdatesCollectionBuilder(context).Set(x => x.MachineGuid == machine.Guid).BuildAsync()).OrderByDescending(x => x.StartDate).ToList(); + UpdatesView = CollectionViewSource.GetDefaultView(Updates); + UpdatesView.Filter = UpdatesFilter; + OnFilterChanged(); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error loading machine updates."); + _notification.ShowError($"An error occurred while loading the history of machine updates.\n{ex.FlattenMessage()}"); + } } #endregion #region Private Methods + private async void Refresh() + { + IsFree = false; + using (_notification.PushTaskItem("Refreshing machine updates...")) + { + await Init(Machine, _context); + } + IsFree = true; + } + private void OnFilterChanged() { if (UpdatesView != null) @@ -119,6 +156,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels if (!DisplayMachineSetups && update.IsSetup) return false; if (!DisplayApplicationUpdates && update.IsUpdate) return false; if (!DisplayDatabaseUpdates && update.IsDataBase) return false; + if (!DisplaySynchronizations && update.IsSynchronization) return false; return true; } else diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineUpdateDetailsDialog.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineUpdateDetailsDialog.xaml index 9d60d36f8..804ee456c 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineUpdateDetailsDialog.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineUpdateDetailsDialog.xaml @@ -40,6 +40,9 @@ + + + diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineUpdatesView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineUpdatesView.xaml index d9ea2cb44..8d1ff8ec8 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineUpdatesView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineUpdatesView.xaml @@ -18,9 +18,9 @@ - + - + @@ -34,11 +34,16 @@ - - Machine Setups - Software Updates - Database Updates - + + + Machine Setups + Software Updates + Database Updates + Synchronizations + + + + @@ -66,6 +71,9 @@ + + + diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs index 11d156292..9992d4e2f 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/EventLogging/DefaultEventLogger.cs @@ -137,7 +137,7 @@ namespace Tango.MachineStudio.Common.EventLogging /// The message. private void Machine_RequestSent(object sender, IMessage message) { - Log(EventTypes.REQUEST_SENT, String.Format("Sending request '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); + //Log(EventTypes.REQUEST_SENT, String.Format("Sending request '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); } /// @@ -147,7 +147,7 @@ namespace Tango.MachineStudio.Common.EventLogging /// The instance containing the event data. private void Machine_RequestFailed(object sender, RequestFailedEventArgs e) { - Log(EventTypes.REQUEST_FAILED, String.Format("Request failed '{0}'...{1}{2}{1}{3}", e.Message.GetType().Name, Environment.NewLine, e.Message.ToJsonString(), e.Exception.ToString())); + //Log(EventTypes.REQUEST_FAILED, String.Format("Request failed '{0}'...{1}{2}{1}{3}", e.Message.GetType().Name, Environment.NewLine, e.Message.ToJsonString(), e.Exception.ToString())); } /// @@ -157,7 +157,7 @@ namespace Tango.MachineStudio.Common.EventLogging /// The message. private void Machine_ResponseReceived(object sender, IMessage message) { - Log(EventTypes.RESPONSE_RECEIVED, String.Format("Response received '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); + //Log(EventTypes.RESPONSE_RECEIVED, String.Format("Response received '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString())); } /// diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/MachineStudioWebClientBase.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/MachineStudioWebClientBase.cs index c2d0dd3e6..72eb4acaa 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/MachineStudioWebClientBase.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Web/MachineStudioWebClientBase.cs @@ -40,6 +40,15 @@ namespace Tango.MachineStudio.Common.Web } + /// + /// Initializes a new instance of the class. + /// + /// Other instance. + public MachineStudioWebClientBase(MachineStudioWebClientBase cloned) : base(cloned) + { + + } + /// /// Executes the CheckForUpdates action and returns Tango.MachineStudio.Common.Web.CheckForUpdatesResponse. /// diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/App.xaml.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/App.xaml.cs index 94f98feb2..c7e634ee8 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/App.xaml.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/App.xaml.cs @@ -35,7 +35,7 @@ namespace Tango.MachineStudio.UI private WpfGlobalExceptionTrapper exceptionTrapper; private LogManager LogManager = LogManager.Default; - public static String[] StartupArgs { get; set; } + public static String[] StartupArgs { get; set; } = Environment.GetCommandLineArgs().Skip(1).ToArray(); protected override void OnStartup(StartupEventArgs e) { diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Authentication/DefaultAuthenticationProvider.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Authentication/DefaultAuthenticationProvider.cs index 3cc5e8b49..c992d0768 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Authentication/DefaultAuthenticationProvider.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Authentication/DefaultAuthenticationProvider.cs @@ -96,7 +96,10 @@ namespace Tango.MachineStudio.UI.Authentication var settings = SettingsManager.Default.GetOrCreate(); - _client.Environment = settings.DeploymentSlot; + if (!App.StartupArgs.Contains("-webDebug")) + { + _client.Environment = settings.DeploymentSlot; + } var appVersion = AssemblyHelper.GetCurrentAssemblyVersion().ToString(); diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs index 3fffd69fd..25843d1c3 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Windows; using Tango.Core.DI; using Tango.Integration.ExternalBridge; @@ -71,7 +72,16 @@ namespace Tango.MachineStudio.UI TangoIOC.Default.Unregister(); - TangoIOC.Default.Register(new MachineStudioWebClient()); + + if (App.StartupArgs.Contains("-webDebug")) + { + TangoIOC.Default.Register(new MachineStudioWebClient("http://localhost:1111", null)); + } + else + { + TangoIOC.Default.Register(new MachineStudioWebClient()); + } + TangoIOC.Default.Register(new DefaultDispatcherProvider(Application.Current.Dispatcher)); TangoIOC.Default.Register(); TangoIOC.Default.Register(); @@ -84,7 +94,7 @@ namespace Tango.MachineStudio.UI TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(new TeamFoundationServiceExtendedClient("https://twinetfs.visualstudio.com", String.Empty, "szzfokrceo4rhd4eqi5qpmxn3pa5iwl3q7tlqd36l2m7smz2ynoa")); - + TangoIOC.Default.Register(); TangoIOC.Default.Register(); diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync-job.png b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync-job.png new file mode 100644 index 000000000..4e46ee447 Binary files /dev/null and b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync-job.png differ diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync.png b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync.png new file mode 100644 index 000000000..46059c5c0 Binary files /dev/null and b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync.png differ diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItem.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItem.cs new file mode 100644 index 000000000..4e3137e1c --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItem.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.PPC.Common.Notifications; + +namespace Tango.PPC.Jobs.NotificationItems +{ + /// + /// Represents a simple text message notification item which can be inserted into the application notifications panel. + /// + /// + public class NewSynchronizardJobsNotificationItem : NotificationItem + { + /// + /// Initializes a new instance of the class. + /// + public NewSynchronizardJobsNotificationItem() + { + CanClose = true; + } + + /// + /// Gets or sets the view type. + /// + public override Type ViewType + { + get { return typeof(NewSynchronizardJobsNotificationItemView); } + } + } +} diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItemView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItemView.xaml new file mode 100644 index 000000000..23a64676b --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItemView.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + New job definitions were synchronized with your machine. Tap to refresh your job list. + + + + + + diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItemView.xaml.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItemView.xaml.cs new file mode 100644 index 000000000..33db09386 --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/NotificationItems/NewSynchronizardJobsNotificationItemView.xaml.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +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.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.PPC.Jobs.NotificationItems +{ + /// + /// Represents the view. + /// + /// + /// + public partial class NewSynchronizardJobsNotificationItemView : UserControl + { + public NewSynchronizardJobsNotificationItemView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj index 609a15de4..02343c0cc 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj @@ -124,6 +124,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -228,6 +232,10 @@ + + + NewSynchronizardJobsNotificationItemView.xaml + Code @@ -486,10 +494,13 @@ + + + - + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs index a097084f7..dac8f9e49 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs @@ -593,6 +593,7 @@ namespace Tango.PPC.Jobs.ViewModels } Job.LastUpdated = DateTime.UtcNow; + Job.IsSynchronized = false; Job.JobStatus = BL.Enumerations.JobStatuses.Draft; await _db.SaveChangesAsync(); _current_job_string = Job.ToJobFileWhenLoaded().ToString(); diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobsViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobsViewVM.cs index 34d7e32cd..e80154413 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobsViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobsViewVM.cs @@ -36,6 +36,8 @@ using System.Windows.Media.Imaging; using Tango.Touch.Components; using Tango.PPC.Jobs.ViewContracts; using Tango.Core.ExtensionMethods; +using Tango.PPC.Common.Synchronization; +using Tango.PPC.Jobs.NotificationItems; namespace Tango.PPC.Jobs.ViewModels { @@ -483,7 +485,7 @@ namespace Tango.PPC.Jobs.ViewModels settings.LastJobType = vm.SelectedJobType; settings.LastJobColorSpace = vm.SelectedColorSpace; - + if (vm.SelectedColorSpace == ColorSpaces.Catalog) { settings.LastSelectedCatalogGuid = catalogVM.SelectedCatalog.Guid; @@ -728,6 +730,8 @@ namespace Tango.PPC.Jobs.ViewModels { _catalogs = (await c.ColorCatalogs.ToListAsync()).ToObservableCollection(); } + + MachineDataSynchronizer.SynchronizationEnded += MachineDataSynchronizer_SynchronizationEnded; } public override void OnNavigatedTo() @@ -871,6 +875,25 @@ namespace Tango.PPC.Jobs.ViewModels #endregion + #region Handle New Synchronized Jobs + + private void MachineDataSynchronizer_SynchronizationEnded(object sender, SynchronizationEndedEventArgs e) + { + if (e.NewChangedJobs > 0) + { + var item = NotificationProvider.PushNotification(); + item.Pressed += (_, __) => + { + LoadJobs(() => + { + NotificationProvider.ShowSuccess("Your job list is now synchronized."); + }); + }; + } + } + + #endregion + #region Color Profile Request private void ExternalBridgeService_ColorProfileRequest(object sender, ColorProfileRequestEventArgs e) diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/ViewModels/MainViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/ViewModels/MainViewVM.cs index 3cceba6c9..1af9b8609 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/ViewModels/MainViewVM.cs @@ -122,6 +122,20 @@ namespace Tango.PPC.MachineSettings.ViewModels set { _defaultSpoolType = value; RaisePropertyChangedAuto(); } } + private bool _synchronizeJobs; + public bool SynchronizeJobs + { + get { return _synchronizeJobs; } + set { _synchronizeJobs = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private bool _synchronizeDiagnostics; + public bool SynchronizeDiagnostics + { + get { return _synchronizeDiagnostics; } + set { _synchronizeDiagnostics = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + #endregion #region Commands @@ -136,12 +150,18 @@ namespace Tango.PPC.MachineSettings.ViewModels /// public RelayCommand DiscardCommand { get; set; } + /// + /// Gets or sets the synchronize command. + /// + public RelayCommand SynchronizeCommand { get; set; } + #endregion public MainViewVM() { SaveCommand = new RelayCommand(Save); DiscardCommand = new RelayCommand(Discard); + SynchronizeCommand = new RelayCommand(Synchronize, () => !MachineDataSynchronizer.IsSynchronizing && IsFree && (SynchronizeJobs || SynchronizeDiagnostics)); } private void Discard() @@ -166,6 +186,11 @@ namespace Tango.PPC.MachineSettings.ViewModels Settings.LockScreenPassword = LockScreenPassword; Settings.DefaultRmlGuid = DefaultRML?.Guid; Settings.DefaultSpoolTypeGuid = DefaultSpoolType?.Guid; + Settings.SynchronizeJobs = SynchronizeJobs; + Settings.SynchronizeDiagnostics = SynchronizeDiagnostics; + + MachineDataSynchronizer.IsEnabled = SynchronizeJobs || SynchronizeDiagnostics; + Settings.Save(); await MachineProvider.SaveMachine(); @@ -186,6 +211,13 @@ namespace Tango.PPC.MachineSettings.ViewModels } + public override void OnApplicationReady() + { + base.OnApplicationReady(); + MachineDataSynchronizer.SynchronizationStarted += (_, __) => InvalidateRelayCommands(); + MachineDataSynchronizer.SynchronizationEnded += (_, __) => InvalidateRelayCommands(); + } + public override void OnNavigatedTo() { base.OnNavigatedTo(); @@ -216,6 +248,9 @@ namespace Tango.PPC.MachineSettings.ViewModels DefaultRML = Adapter.Rmls.SingleOrDefault(x => x.Guid == Settings.DefaultRmlGuid); DefaultSpoolType = Adapter.SpoolTypes.SingleOrDefault(x => x.Guid == Settings.DefaultSpoolTypeGuid); + + SynchronizeJobs = Settings.SynchronizeJobs; + SynchronizeDiagnostics = Settings.SynchronizeDiagnostics; } private async void OnEnableRemoteAssistanceChanged() @@ -290,5 +325,29 @@ namespace Tango.PPC.MachineSettings.ViewModels { ExternalBridgeService.Enabled = EnableExternalBridge; } + + private async void Synchronize() + { + try + { + IsFree = false; + NotificationProvider.SetGlobalBusyMessage("Synchronizing..."); + + await MachineDataSynchronizer.Synchronize(); + + NotificationProvider.ReleaseGlobalBusyMessage(); + await NotificationProvider.ShowSuccess("Synchronization completed successfully."); + } + catch (Exception ex) + { + NotificationProvider.ReleaseGlobalBusyMessage(); + await NotificationProvider.ShowError($"Error occurred while trying to synchronize.\n{ex.FlattenMessage()}"); + } + finally + { + NotificationProvider.ReleaseGlobalBusyMessage(); + IsFree = true; + } + } } } diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml index 0d9a3cea1..e8bd657a0 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.MachineSettings/Views/MainView.xaml @@ -11,9 +11,9 @@ xmlns:global="clr-namespace:Tango.PPC.MachineSettings" xmlns:local="clr-namespace:Tango.PPC.MachineSettings.Views" mc:Ignorable="d" - d:DesignHeight="2500" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:MainViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.MainViewVM}"> + d:DesignHeight="3000" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:MainViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.MainViewVM}"> - + @@ -195,6 +195,45 @@ + + + + + + + Synchronize Jobs + + + + Synchronize your jobs with twine's cloud services. + + + + + + + + + Synchronize Diagnostics Data + + + + Help us improve your experience using this system. + + + + + + + + + + Once enabled, synchronization occurres automatically in the background. you can choose to synchronize right now. + + + Synchronize Now + + diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Images/sync.png b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Images/sync.png new file mode 100644 index 000000000..46059c5c0 Binary files /dev/null and b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Images/sync.png differ diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Tango.PPC.Technician.csproj b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Tango.PPC.Technician.csproj index 192b9ae11..e4261334a 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Tango.PPC.Technician.csproj +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Tango.PPC.Technician.csproj @@ -98,10 +98,14 @@ + CatalogView.xaml + + SynchronizationView.xaml + PackagesView.xaml @@ -191,6 +195,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -234,5 +242,8 @@ + + + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModelLocator.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModelLocator.cs index c79a10e79..05a04e2a6 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModelLocator.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModelLocator.cs @@ -21,6 +21,7 @@ namespace Tango.PPC.Technician TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); + TangoIOC.Default.Register(); } /// @@ -88,5 +89,16 @@ namespace Tango.PPC.Technician return TangoIOC.Default.GetInstance(); } } + + /// + /// Gets the synchronization view vm. + /// + public static SynchronizationViewVM SynchronizationViewVM + { + get + { + return TangoIOC.Default.GetInstance(); + } + } } } diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModels/SynchronizationViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModels/SynchronizationViewVM.cs new file mode 100644 index 000000000..8036b99a4 --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModels/SynchronizationViewVM.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core.Commands; +using Tango.PPC.Common; + +namespace Tango.PPC.Technician.ViewModels +{ + public class SynchronizationViewVM : PPCViewModel + { + public RelayCommand SynchronizeCommand { get; set; } + + public SynchronizationViewVM() + { + SynchronizeCommand = new RelayCommand(Synchronize, () => !MachineDataSynchronizer.IsSynchronizing); + } + + public override void OnApplicationStarted() + { + + } + + public override void OnApplicationReady() + { + base.OnApplicationReady(); + MachineDataSynchronizer.SynchronizationStarted += (_, __) => InvalidateRelayCommands(); + MachineDataSynchronizer.SynchronizationEnded += (_, __) => InvalidateRelayCommands(); + } + + private async void Synchronize() + { + try + { + await MachineDataSynchronizer.Synchronize(); + } + catch { } + } + } +} diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModels/SystemViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModels/SystemViewVM.cs index b46e566d1..13aeeb671 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModels/SystemViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/ViewModels/SystemViewVM.cs @@ -265,11 +265,11 @@ namespace Tango.PPC.Technician.ViewModels { using (ObservablesContext db = ObservablesContext.CreateDefault()) { - var jobs = await db.Jobs.Include(x => x.JobRuns).Where(x => x.MachineGuid == MachineProvider.Machine.Guid).ToListAsync(); + var jobRuns = await db.JobRuns.Where(x => x.MachineGuid == MachineProvider.Machine.Guid).ToListAsync(); - TotalDyeTime = TimeSpan.FromHours(jobs.SelectMany(x => x.JobRuns).Select(x => x.EndDate - x.StartDate).Sum(x => x.TotalHours)).ToString(@"hh\:mm\:ss"); + TotalDyeTime = TimeSpan.FromHours(jobRuns.Select(x => x.EndDate - x.StartDate).Sum(x => x.TotalHours)).ToString(@"hh\:mm\:ss"); - int meters = (int)jobs.SelectMany(x => x.JobRuns).Select(x => x.EndPosition).Sum(); + int meters = (int)jobRuns.Select(x => x.EndPosition).Sum(); TotalDyeMeters = $"{meters.ToString("N0")} meters"; } } diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/CatalogView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/CatalogView.xaml index c13c3e3eb..b0a86d938 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/CatalogView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/CatalogView.xaml @@ -50,7 +50,7 @@ Logging - + Display and investigate issues using application and embedded device logs. @@ -62,7 +62,7 @@ System - + Display system properties, perform system actions, reset, shutdown etc... @@ -74,7 +74,7 @@ Dispensers - + Perform manual dispensers homing priming. @@ -86,12 +86,24 @@ Installed Packages - + View the history of update packages installation. + + + + + + Synchronization + + View the current status and history of synchronization operations to the machine service. + + + + diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/MainView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/MainView.xaml index 733a2d713..2acadb32d 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/MainView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/MainView.xaml @@ -18,6 +18,7 @@ + diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/SynchronizationView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/SynchronizationView.xaml new file mode 100644 index 000000000..a0021d40e --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/SynchronizationView.xaml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + Synchronization + + + + + + + + Synchronize Now + Synchronization occurres automatically in the background. You can choose to synchronize now. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/SynchronizationView.xaml.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/SynchronizationView.xaml.cs new file mode 100644 index 000000000..7193d66ff --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Views/SynchronizationView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +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.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.PPC.Technician.Views +{ + /// + /// Interaction logic for SynchronizationView.xaml + /// + public partial class SynchronizationView : UserControl + { + public SynchronizationView() + { + InitializeComponent(); + } + } +} 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 1474eaa04..8f1a80d37 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Application/IPPCApplicationManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Application/IPPCApplicationManager.cs @@ -95,6 +95,11 @@ namespace Tango.PPC.Common.Application /// Version Version { get; } + /// + /// Gets the firmware version. + /// + Version FirmwareVersion { get; } + /// /// Gets the application build date. /// 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 8cccc43a7..eae09a7e7 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Connection/DefaultMachineProvider.cs @@ -134,6 +134,12 @@ namespace Tango.PPC.Common.Connection try { await MachineOperator.Connect(); + + if (MachineOperator.DeviceInformation != null) + { + settings.FirmwareVersion = MachineOperator.DeviceInformation.Version; + settings.Save(); + } } catch (Exception) { @@ -156,6 +162,12 @@ namespace Tango.PPC.Common.Connection try { await MachineOperator.Connect(); + + if (MachineOperator.DeviceInformation != null) + { + settings.FirmwareVersion = MachineOperator.DeviceInformation.Version; + settings.Save(); + } } catch (Exception) { @@ -186,6 +198,12 @@ 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); } 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 f1c722d96..dfa9b833b 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineSetup/MachineSetupManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineSetup/MachineSetupManager.cs @@ -44,6 +44,7 @@ namespace Tango.PPC.Common.MachineSetup private IOperationSystemManager _windows_manager; private PPCWebClient _client; private List _logs; + private bool _isUpdating; #region Events @@ -93,7 +94,10 @@ namespace Tango.PPC.Common.MachineSetup private void LogManager_NewLog(object sender, LogItemBase e) { - _logs.Add(e); + if (_isUpdating) + { + _logs.Add(e); + } } #endregion @@ -136,6 +140,8 @@ namespace Tango.PPC.Common.MachineSetup LogManager.Log(xx, "Error notifying setup completed."); } } + + _isUpdating = false; } private async void OnCompleted(MachineSetupResult result, TaskCompletionSource completionSource, MachineSetupResponse response) @@ -157,6 +163,8 @@ namespace Tango.PPC.Common.MachineSetup LogManager.Log(xx, "Error notifying setup completed."); } } + + _isUpdating = false; } private String GetLogsStringAndClear() @@ -186,6 +194,8 @@ namespace Tango.PPC.Common.MachineSetup try { + _isUpdating = true; + LogManager.Log($"Starting machine setup for serial number {serialNumber}..."); var machineServiceAddress = SettingsManager.Default.GetOrCreate().GetMachineServiceAddress(); 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 7e742ceb6..30abb561f 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/MachineUpdate/MachineUpdateManager.cs @@ -880,6 +880,8 @@ namespace Tango.PPC.Common.MachineUpdate UpdateDBRequest request = new UpdateDBRequest(); request.SerialNumber = serialNumber; + request.ApplicationVersion = _app_manager.Version.ToString(); + request.FirmwareVersion = _app_manager.FirmwareVersion.ToString(); UpdateDBResponse update_response = null; diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs index d791d10d3..8805b75e4 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCSettings.cs @@ -179,6 +179,26 @@ namespace Tango.PPC.Common /// public String PreviousApplicationVersion { get; set; } + /// + /// Gets or sets a value indicating whether synchronize jobs with twine server. + /// + public bool SynchronizeJobs { get; set; } + + /// + /// Gets or sets a value indicating whether synchronize diagnostics data. + /// + public bool SynchronizeDiagnostics { get; set; } + + /// + /// Gets or sets the synchronization interval. + /// + public TimeSpan SynchronizationInterval { get; set; } + + /// + /// Gets or sets the known firmware version. + /// + public String FirmwareVersion { get; set; } + /// /// Gets the machine service address. /// @@ -214,6 +234,10 @@ namespace Tango.PPC.Common SupportedColorSpaces = new List(); SupportedJobTypes = new List(); PreviousApplicationVersion = "1.0.0.0"; + SynchronizeJobs = true; + SynchronizeDiagnostics = true; + SynchronizationInterval = TimeSpan.FromMinutes(60); + FirmwareVersion = "1.0.0.0"; } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs index 19bc6cd67..7992e6672 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs @@ -17,6 +17,7 @@ using Tango.PPC.Common.Notifications; using Tango.PPC.Common.Printing; using Tango.PPC.Common.RemoteAssistance; using Tango.PPC.Common.Storage; +using Tango.PPC.Common.Synchronization; using Tango.Settings; using Tango.SharedUI; using static Tango.SharedUI.Controls.NavigationControl; @@ -109,6 +110,12 @@ namespace Tango.PPC.Common [TangoInject] public IEventLogger EventLogger { get; set; } + /// + /// Gets or sets the machine data synchronizer. + /// + [TangoInject] + public IMachineDataSynchronizer MachineDataSynchronizer { get; set; } + private PPCSettings _settings; /// /// Gets the main PPC settings. 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..8260eb4b3 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/DefaultMachineDataSynchronizer.cs @@ -0,0 +1,554 @@ +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 _logs; + private bool _synchronizedOnce; + + [TangoInject(TangoInjectMode.WhenAvailable)] + private IPPCApplicationManager _appManager; + + public event EventHandler CurrentStatusChanged; + public event EventHandler SynchronizationStarted; + public event EventHandler SynchronizationEnded; + + public int MaxJobs { get; set; } + public int MaxJobRuns { get; set; } + public int MaxMachinesEvents { 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 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(); + + MaxJobs = 10; + MaxJobRuns = 100; + MaxMachinesEvents = 100; + + var settings = SettingsManager.Default.GetOrCreate(); + 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(); + _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 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 dtos = new List(); + + 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 dtos = new List(); + + 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 dtos = new List(); + + foreach (var machineEvent in machineEvents) + { + machineEvent.IsSynchronized = true; + var dto = MachinesEventDTO.FromObservable(machineEvent); + request.MachineEvents.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); + } + } + + await db.SaveChangesAsync(); + } + } + + private async Task DownloadMachineData(bool syncJobs, bool syncDiagnostics) + { + return await client.DownloadMachineData(new DownloadMachineDataRequest() + { + RequestJobs = syncJobs, + RequestJobRuns = syncDiagnostics, + RequestMachineEvents = syncDiagnostics, + MaxJobs = MaxJobs, + MaxJobRuns = MaxJobRuns, + MaxMachinesEvents = MaxMachinesEvents, + }); + } + + private async Task 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 = _authenticationProvider.CurrentUser.Guid; + 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 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 = _authenticationProvider.CurrentUser.Guid; + 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(); + + var syncJobs = settings.SynchronizeJobs; + var syncDiagnostics = settings.SynchronizeDiagnostics; + + if (!syncJobs && !syncDiagnostics) return; + + 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, + SerialNumber = _machineProvider.Machine.SerialNumber, + }); + } + + 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}"); + 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}"); + 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 SynchronizationEnded; + event EventHandler CurrentStatusChanged; + int MaxJobs { get; set; } + int MaxJobRuns { get; set; } + int MaxMachinesEvents { get; set; } + SynchronizationStatus CurrentStatus { get; } + SynchronizationStatus LastStatus { get; } + SynchronizedObservableCollection 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/Tango.PPC.Common.csproj b/Software/Visual_Studio/PPC/Tango.PPC.Common/Tango.PPC.Common.csproj index d38b9c8e9..b781e3c9a 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 @@ -156,6 +156,12 @@ + + + + + + @@ -177,6 +183,8 @@ + + @@ -192,6 +200,7 @@ + @@ -226,8 +235,12 @@ + + + + @@ -406,7 +419,7 @@ - + \ No newline at end of file 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..66fca8e36 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataRequest.cs @@ -0,0 +1,22 @@ +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 int MaxJobs { get; set; } + public int MaxJobRuns { get; set; } + public int MaxMachinesEvents { 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..5c3f7ba78 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/DownloadMachineDataResponse.cs @@ -0,0 +1,25 @@ +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 Jobs { get; set; } + public List JobRuns { get; set; } + public List MachineEvents { get; set; } + + public DownloadMachineDataResponse() + { + Jobs = new List(); + JobRuns = new List(); + MachineEvents = new List(); + } + } +} 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..fc135c234 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedRequest.cs @@ -0,0 +1,25 @@ +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 SynchronizedJobs { get; set; } + public List SynchronizedJobRuns { get; set; } + public List SynchronizedMachineEvents { get; set; } + + public NotifyMachineDataDownloadCompletedRequest() + { + SynchronizedJobs = new List(); + SynchronizedJobRuns = new List(); + SynchronizedMachineEvents = new List(); + } + } +} 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 5520f8b5a..ff972acb2 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs @@ -40,6 +40,15 @@ namespace Tango.PPC.Common.Web } + /// + /// Initializes a new instance of the class. + /// + /// Other instance. + public PPCWebClientBase(PPCWebClientBase cloned) : base(cloned) + { + + } + /// /// Executes the MachineSetup action and returns Tango.PPC.Common.Web.MachineSetupResponse. /// @@ -85,6 +94,33 @@ namespace Tango.PPC.Common.Web return Post("UpdateDB", request); } + /// + /// Executes the UploadMachineData action and returns Tango.PPC.Common.Web.UploadMachineDataResponse. + /// + /// + public Task UploadMachineData(Tango.PPC.Common.Web.UploadMachineDataRequest request) + { + return Post("UploadMachineData", request); + } + + /// + /// Executes the DownloadMachineData action and returns Tango.PPC.Common.Web.DownloadMachineDataResponse. + /// + /// + public Task DownloadMachineData(Tango.PPC.Common.Web.DownloadMachineDataRequest request) + { + return Post("DownloadMachineData", request); + } + + /// + /// Executes the NotifyMachineDataDownloadCompleted action and returns Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedResponse. + /// + /// + public Task NotifyMachineDataDownloadCompleted(Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedRequest request) + { + return Post("NotifyMachineDataDownloadCompleted", request); + } + /// /// Executes the GetLatestVersion action and returns Tango.PPC.Common.Web.LatestVersionResponse. /// 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..c78f6199e 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs @@ -10,5 +10,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/UploadMachineDataRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataRequest.cs new file mode 100644 index 000000000..dc0b05988 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataRequest.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 UploadMachineDataRequest : WebRequestMessage + { + public List Jobs { get; set; } + public List JobRuns { get; set; } + public List MachineEvents { get; set; } + public String ApplicationVersion { get; set; } + public String FirmwareVersion { get; set; } + + public UploadMachineDataRequest() + { + Jobs = new List(); + JobRuns = new List(); + MachineEvents = new List(); + } + } +} 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..e4dda4013 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UploadMachineDataResponse.cs @@ -0,0 +1,26 @@ +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 FailedJobs { get; set; } + public List FailedJobRuns { get; set; } + public List FailedMachineEvents { get; set; } + public String NotifyCompletedToken { get; set; } + + public UploadMachineDataResponse() + { + FailedJobs = new List(); + FailedJobRuns = new List(); + FailedMachineEvents = new List(); + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/App.config b/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/App.config new file mode 100644 index 000000000..731f6de6c --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Program.cs b/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Program.cs new file mode 100644 index 000000000..d77192de2 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Program.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.SQLExaminer; + +namespace Tango.PPC.SchemaSynchronizer.CLI +{ + class Program + { + static void Main(string[] args) + { + Core.DataSource source = new Core.DataSource(); + source.Address = "localhost\\SQLEXPRESS"; + source.Catalog = "Tango"; + + Core.DataSource target = new Core.DataSource(); + target.Address = "localhost\\SQLPPC"; + target.Catalog = "Tango"; + + ExaminerConfigurationBuilder builder = new ExaminerConfigurationBuilder(ExaminerConfigurationType.Schema); + + builder. + SetSource(source). + SetTarget(target). + Synchronize(); + + var config = builder.Build(); + + ExaminerProcess process = new ExaminerProcess(config, ExaminerProcessType.Schema); + process.Progress += (x, msg) => + { + Console.WriteLine(msg); + }; + var result = process.Execute().Result; + + if (result.ExitCode == ExaminerProcessExitCode.Success) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("Completed!"); + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed!"); + } + + Console.ReadLine(); + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Properties/AssemblyInfo.cs b/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..61246489d --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Tango.PPC.SchemaSynchronizer.CLI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Tango.PPC.SchemaSynchronizer.CLI")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f3746f2b-e4ae-498b-9d42-74f95d992460")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Tango.PPC.SchemaSynchronizer.CLI.csproj b/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Tango.PPC.SchemaSynchronizer.CLI.csproj new file mode 100644 index 000000000..529815ba2 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.SchemaSynchronizer.CLI/Tango.PPC.SchemaSynchronizer.CLI.csproj @@ -0,0 +1,63 @@ + + + + + Debug + AnyCPU + {F3746F2B-E4AE-498B-9D42-74F95D992460} + Exe + Tango.PPC.SchemaSynchronizer.CLI + Tango.PPC.SchemaSynchronizer.CLI + v4.6.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + {a34ee0f0-649d-41c8-8489-b6f1cc6924ee} + Tango.Core + + + {e1e66ed9-597d-45fa-8048-de90a6930484} + Tango.SQLExaminer + + + + \ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs index 44b6ffded..70ba4b695 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs @@ -33,6 +33,7 @@ using Tango.Core.Threading; using Tango.PPC.Common.Messages; using Tango.Core.ExtensionMethods; using Tango.PPC.Common.Navigation; +using Tango.PPC.Common.Synchronization; namespace Tango.PPC.UI.PPCApplication { @@ -50,6 +51,7 @@ namespace Tango.PPC.UI.PPCApplication private IEventLogger _eventLogger; private IPPCModuleLoader _moduleLoader; private INotificationProvider _notificationProvider; + private IMachineDataSynchronizer _machineDataSynchronizer; private WatchDogServer _watchdogServer; private ObservablesContext _machineContext; private ActionTimer _screenLockTimer; @@ -146,16 +148,28 @@ namespace Tango.PPC.UI.PPCApplication set { _isScreenLocked = value; RaisePropertyChangedAuto(); } } + /// + /// Gets the firmware version. + /// + public Version FirmwareVersion + { + get + { + return Version.Parse(SettingsManager.Default.GetOrCreate().FirmwareVersion); + } + } + /// /// Initializes a new instance of the class. /// - public DefaultPPCApplicationManager(IMachineProvider machineProvider, IDispatcherProvider dispatcherProvider, IEventLogger eventLogger, IPPCModuleLoader moduleLoader, INotificationProvider notificationProvider) + public DefaultPPCApplicationManager(IMachineProvider machineProvider, IDispatcherProvider dispatcherProvider, IEventLogger eventLogger, IPPCModuleLoader moduleLoader, INotificationProvider notificationProvider, IMachineDataSynchronizer machineDataSynchronizer) { _notificationProvider = notificationProvider; _machineProvider = machineProvider; _dispatcher = dispatcherProvider; _eventLogger = eventLogger; _moduleLoader = moduleLoader; + _machineDataSynchronizer = machineDataSynchronizer; if (!DesignMode) { @@ -248,6 +262,8 @@ namespace Tango.PPC.UI.PPCApplication LogManager.Log("Loading machine from database..."); _machineContext = ObservablesContext.CreateDefault(); _machine = new MachineBuilder(_machineContext).SetFirst().WithVersion().WithSettings().WithOrganization().WithConfiguration().WithSpools().WithCats().Build(); + + } initialized = true; @@ -349,6 +365,9 @@ namespace Tango.PPC.UI.PPCApplication LogManager.Log("Initializing Machine Provider..."); _machineProvider.Init(_machine, _machineContext); + LogManager.Log("Starting Machine Data Synchronizer..."); + _machineDataSynchronizer.IsEnabled = true; + LogManager.Log("Applications initialization completed!"); LogManager.Log("Checking for un-notified PPC view models..."); diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs index 60bce4e20..49157a998 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/ViewModelLocator.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Windows; using Tango.Core.DI; using Tango.Integration.ExternalBridge; @@ -21,6 +22,7 @@ using Tango.PPC.Common.OS; using Tango.PPC.Common.Printing; using Tango.PPC.Common.RemoteAssistance; using Tango.PPC.Common.Storage; +using Tango.PPC.Common.Synchronization; using Tango.PPC.Common.Threading; using Tango.PPC.Common.UpdatePackages; using Tango.PPC.Common.UWF; @@ -75,8 +77,17 @@ namespace Tango.PPC.UI TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); TangoIOC.Default.Unregister(); + TangoIOC.Default.Unregister(); + + if (App.StartupArgs.Contains("-webDebug")) + { + TangoIOC.Default.Register(new PPCWebClient("http://localhost:1111", null)); + } + else + { + TangoIOC.Default.Register(new PPCWebClient()); + } - TangoIOC.Default.Register(new PPCWebClient()); TangoIOC.Default.Register(new DefaultDispatcherProvider(Application.Current.Dispatcher)); TangoIOC.Default.Register(); TangoIOC.Default.Register(); @@ -84,6 +95,7 @@ namespace Tango.PPC.UI TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); + TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); TangoIOC.Default.Register(); diff --git a/Software/Visual_Studio/Tango.BL/Builders/EntityCollectionBuilderBase.cs b/Software/Visual_Studio/Tango.BL/Builders/EntityCollectionBuilderBase.cs index a0cd24511..d4d65c16d 100644 --- a/Software/Visual_Studio/Tango.BL/Builders/EntityCollectionBuilderBase.cs +++ b/Software/Visual_Studio/Tango.BL/Builders/EntityCollectionBuilderBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -13,6 +14,7 @@ namespace Tango.BL.Builders private List> _steps; private List, IQueryable>>> _querySteps; private bool _entity_set; + private Func, IQueryable> _appendQuery; protected IEnumerable Entities { get; set; } @@ -38,6 +40,11 @@ namespace Tango.BL.Builders query = queryStep.Value(query); } + if (_appendQuery != null) + { + query = _appendQuery(query); + } + Entities = query.ToList(); }); @@ -58,6 +65,11 @@ namespace Tango.BL.Builders query = queryStep.Value(query); } + if (_appendQuery != null) + { + query = _appendQuery(query); + } + Entities = query.ToList(); }); @@ -71,6 +83,12 @@ namespace Tango.BL.Builders return query; } + public TBuilder Query(Func, IQueryable> query) + { + _appendQuery = query; + return this as TBuilder; + } + protected void CommitSteps() { foreach (var step in _steps.ToList().DistinctBy(x => x.Key).OrderBy(x => x.Key)) @@ -104,6 +122,18 @@ namespace Tango.BL.Builders return Entities.ToSynchronizedObservableCollection(); } + public List BuildList() + { + if (!_entity_set) + { + throw new InvalidOperationException("Could not build entity. Entity was not set."); + } + + CommitSteps(); + + return Entities.ToList(); + } + public Task> BuildAsync() { return Task.Factory.StartNew>(() => @@ -111,5 +141,13 @@ namespace Tango.BL.Builders return Build(); }); } + + public Task> BuildListAsync() + { + return Task.Factory.StartNew>(() => + { + return BuildList(); + }); + } } } diff --git a/Software/Visual_Studio/Tango.BL/DTO/JobDTO.cs b/Software/Visual_Studio/Tango.BL/DTO/JobDTO.cs index 137d7b4b3..7ee6c9bb0 100644 --- a/Software/Visual_Studio/Tango.BL/DTO/JobDTO.cs +++ b/Software/Visual_Studio/Tango.BL/DTO/JobDTO.cs @@ -9,6 +9,11 @@ namespace Tango.BL.DTO { public class JobDTO : JobDTOBase { + public List Segments { get; set; } + public JobDTO() + { + Segments = new List(); + } } } diff --git a/Software/Visual_Studio/Tango.BL/DTO/JobDTOBase.cs b/Software/Visual_Studio/Tango.BL/DTO/JobDTOBase.cs index f2842c4fd..c3c418f1f 100644 --- a/Software/Visual_Studio/Tango.BL/DTO/JobDTOBase.cs +++ b/Software/Visual_Studio/Tango.BL/DTO/JobDTOBase.cs @@ -274,5 +274,13 @@ namespace Tango.BL.DTO get; set; } + /// + /// is synchronized + /// + public Boolean IsSynchronized + { + get; set; + } + } } diff --git a/Software/Visual_Studio/Tango.BL/DTO/JobRunDTOBase.cs b/Software/Visual_Studio/Tango.BL/DTO/JobRunDTOBase.cs index 9726f16a7..b47ce3ea9 100644 --- a/Software/Visual_Studio/Tango.BL/DTO/JobRunDTOBase.cs +++ b/Software/Visual_Studio/Tango.BL/DTO/JobRunDTOBase.cs @@ -82,5 +82,13 @@ namespace Tango.BL.DTO get; set; } + /// + /// is synchronized + /// + public Boolean IsSynchronized + { + get; set; + } + } } diff --git a/Software/Visual_Studio/Tango.BL/DTO/MachinesEventDTOBase.cs b/Software/Visual_Studio/Tango.BL/DTO/MachinesEventDTOBase.cs index 8a044f7e8..e990f2a31 100644 --- a/Software/Visual_Studio/Tango.BL/DTO/MachinesEventDTOBase.cs +++ b/Software/Visual_Studio/Tango.BL/DTO/MachinesEventDTOBase.cs @@ -69,5 +69,13 @@ namespace Tango.BL.DTO get; set; } + /// + /// is synchronized + /// + public Boolean IsSynchronized + { + get; set; + } + } } diff --git a/Software/Visual_Studio/Tango.BL/DTO/SegmentDTO.cs b/Software/Visual_Studio/Tango.BL/DTO/SegmentDTO.cs index 1db99be23..09f581db2 100644 --- a/Software/Visual_Studio/Tango.BL/DTO/SegmentDTO.cs +++ b/Software/Visual_Studio/Tango.BL/DTO/SegmentDTO.cs @@ -9,6 +9,11 @@ namespace Tango.BL.DTO { public class SegmentDTO : SegmentDTOBase { + public List BrushStops { get; set; } + public SegmentDTO() + { + BrushStops = new List(); + } } } diff --git a/Software/Visual_Studio/Tango.BL/Entities/Job.cs b/Software/Visual_Studio/Tango.BL/Entities/Job.cs index 19f374951..d2a416271 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/Job.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/Job.cs @@ -335,6 +335,7 @@ namespace Tango.BL.Entities cloned.Name = Name + " - Copy"; cloned.CreationDate = DateTime.UtcNow; + cloned.IsSynchronized = false; cloned.LastRun = null; cloned.ColorSpace = ColorSpace; cloned.Customer = Customer; diff --git a/Software/Visual_Studio/Tango.BL/Entities/JobBase.cs b/Software/Visual_Studio/Tango.BL/Entities/JobBase.cs index b356ecbfe..599e379a8 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/JobBase.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/JobBase.cs @@ -81,6 +81,8 @@ namespace Tango.BL.Entities public event EventHandler LengthPercentageFactorChanged; + public event EventHandler IsSynchronizedChanged; + public event EventHandler ColorCatalogChanged; public event EventHandler ColorSpaceChanged; @@ -993,6 +995,33 @@ namespace Tango.BL.Entities } } + protected Boolean _issynchronized; + + /// + /// Gets or sets the jobbase is synchronized. + /// + + [Column("IS_SYNCHRONIZED")] + + public Boolean IsSynchronized + { + get + { + return _issynchronized; + } + + set + { + if (_issynchronized != value) + { + _issynchronized = value; + + OnIsSynchronizedChanged(value); + + } + } + } + protected ColorCatalog _colorcatalog; /// @@ -1499,6 +1528,15 @@ namespace Tango.BL.Entities RaisePropertyChanged(nameof(LengthPercentageFactor)); } + /// + /// Called when the IsSynchronized has changed. + /// + protected virtual void OnIsSynchronizedChanged(Boolean issynchronized) + { + IsSynchronizedChanged?.Invoke(this, issynchronized); + RaisePropertyChanged(nameof(IsSynchronized)); + } + /// /// Called when the ColorCatalog has changed. /// diff --git a/Software/Visual_Studio/Tango.BL/Entities/JobRunBase.cs b/Software/Visual_Studio/Tango.BL/Entities/JobRunBase.cs index 16351179f..c90aa5eab 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/JobRunBase.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/JobRunBase.cs @@ -41,6 +41,8 @@ namespace Tango.BL.Entities public event EventHandler FailedMessageChanged; + public event EventHandler IsSynchronizedChanged; + protected String _machineguid; /// @@ -228,6 +230,33 @@ namespace Tango.BL.Entities } } + protected Boolean _issynchronized; + + /// + /// Gets or sets the jobrunbase is synchronized. + /// + + [Column("IS_SYNCHRONIZED")] + + public Boolean IsSynchronized + { + get + { + return _issynchronized; + } + + set + { + if (_issynchronized != value) + { + _issynchronized = value; + + OnIsSynchronizedChanged(value); + + } + } + } + /// /// Called when the StartDate has changed. /// @@ -273,6 +302,15 @@ namespace Tango.BL.Entities RaisePropertyChanged(nameof(FailedMessage)); } + /// + /// Called when the IsSynchronized has changed. + /// + protected virtual void OnIsSynchronizedChanged(Boolean issynchronized) + { + IsSynchronizedChanged?.Invoke(this, issynchronized); + RaisePropertyChanged(nameof(IsSynchronized)); + } + /// /// Initializes a new instance of the class. /// diff --git a/Software/Visual_Studio/Tango.BL/Entities/MachinesEventBase.cs b/Software/Visual_Studio/Tango.BL/Entities/MachinesEventBase.cs index 5091267e8..0a3377dce 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/MachinesEventBase.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/MachinesEventBase.cs @@ -32,6 +32,8 @@ namespace Tango.BL.Entities public event EventHandler DescriptionChanged; + public event EventHandler IsSynchronizedChanged; + public event EventHandler EventTypeChanged; public event EventHandler MachineChanged; @@ -197,6 +199,33 @@ namespace Tango.BL.Entities } } + protected Boolean _issynchronized; + + /// + /// Gets or sets the machineseventbase is synchronized. + /// + + [Column("IS_SYNCHRONIZED")] + + public Boolean IsSynchronized + { + get + { + return _issynchronized; + } + + set + { + if (_issynchronized != value) + { + _issynchronized = value; + + OnIsSynchronizedChanged(value); + + } + } + } + protected EventType _eventtype; /// @@ -320,6 +349,15 @@ namespace Tango.BL.Entities RaisePropertyChanged(nameof(Description)); } + /// + /// Called when the IsSynchronized has changed. + /// + protected virtual void OnIsSynchronizedChanged(Boolean issynchronized) + { + IsSynchronizedChanged?.Invoke(this, issynchronized); + RaisePropertyChanged(nameof(IsSynchronized)); + } + /// /// Called when the EventType has changed. /// diff --git a/Software/Visual_Studio/Tango.BL/Entities/TangoUpdate.cs b/Software/Visual_Studio/Tango.BL/Entities/TangoUpdate.cs index 5c254a6a9..4ab67b7c5 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/TangoUpdate.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/TangoUpdate.cs @@ -65,6 +65,19 @@ namespace Tango.BL.Entities } } + [NotMapped] + [JsonIgnore] + public bool IsSynchronization + { + get + { + return + UpdateStatus == TangoUpdateStatuses.SynchronizationStarted || + UpdateStatus == TangoUpdateStatuses.SynchronizationCompleted || + UpdateStatus == TangoUpdateStatuses.SynchronizationFailed; + } + } + [NotMapped] [JsonIgnore] public bool IsStarted @@ -74,7 +87,8 @@ namespace Tango.BL.Entities return UpdateStatus == TangoUpdateStatuses.SetupStarted || UpdateStatus == TangoUpdateStatuses.UpdateStarted || - UpdateStatus == TangoUpdateStatuses.DatabaseStarted; + UpdateStatus == TangoUpdateStatuses.DatabaseStarted || + UpdateStatus == TangoUpdateStatuses.SynchronizationStarted; } } @@ -87,7 +101,8 @@ namespace Tango.BL.Entities return UpdateStatus == TangoUpdateStatuses.SetupCompleted || UpdateStatus == TangoUpdateStatuses.UpdateCompleted || - UpdateStatus == TangoUpdateStatuses.DatabaseCompleted; + UpdateStatus == TangoUpdateStatuses.DatabaseCompleted || + UpdateStatus == TangoUpdateStatuses.SynchronizationCompleted; } } @@ -100,7 +115,8 @@ namespace Tango.BL.Entities return UpdateStatus == TangoUpdateStatuses.SetupFailed || UpdateStatus == TangoUpdateStatuses.UpdateFailed || - UpdateStatus == TangoUpdateStatuses.DatabaseFailed; + UpdateStatus == TangoUpdateStatuses.DatabaseFailed || + UpdateStatus == TangoUpdateStatuses.SynchronizationFailed; } } diff --git a/Software/Visual_Studio/Tango.BL/Enumerations/TangoUpdateStatuses.cs b/Software/Visual_Studio/Tango.BL/Enumerations/TangoUpdateStatuses.cs index 30d324317..855d8b29b 100644 --- a/Software/Visual_Studio/Tango.BL/Enumerations/TangoUpdateStatuses.cs +++ b/Software/Visual_Studio/Tango.BL/Enumerations/TangoUpdateStatuses.cs @@ -16,7 +16,7 @@ namespace Tango.BL.Enumerations [Description("Setup failed")] SetupFailed = 2, - [Description("Software updated started but did not complete")] + [Description("Software update started but did not complete")] UpdateStarted = 100, [Description("Software update completed successfully")] UpdateCompleted = 101, @@ -29,5 +29,12 @@ namespace Tango.BL.Enumerations DatabaseCompleted = 201, [Description("Database update failed")] DatabaseFailed = 202, + + [Description("Synchronization started but did not complete")] + SynchronizationStarted = 300, + [Description("Synchronization completed successfully")] + SynchronizationCompleted = 301, + [Description("Synchronization failed")] + SynchronizationFailed = 302, } } diff --git a/Software/Visual_Studio/Tango.BL/IObservableEntity.cs b/Software/Visual_Studio/Tango.BL/IObservableEntity.cs index 244486add..96a150b4a 100644 --- a/Software/Visual_Studio/Tango.BL/IObservableEntity.cs +++ b/Software/Visual_Studio/Tango.BL/IObservableEntity.cs @@ -90,5 +90,10 @@ namespace Tango.BL /// /// The context. void Delete(ObservablesContext context); + + /// + /// Called when before entity is saved by the context. + /// + void OnBeforeSave(); } } diff --git a/Software/Visual_Studio/Tango.BL/ObservableEntity.cs b/Software/Visual_Studio/Tango.BL/ObservableEntity.cs index 982b853f9..e9bb711da 100644 --- a/Software/Visual_Studio/Tango.BL/ObservableEntity.cs +++ b/Software/Visual_Studio/Tango.BL/ObservableEntity.cs @@ -620,6 +620,18 @@ namespace Tango.BL #endregion + #region Virtual Methods + + /// + /// Called when before entity is saved by the context. + /// + public virtual void OnBeforeSave() + { + + } + + #endregion + #region INotify Property Changed /// diff --git a/Software/Visual_Studio/Tango.BL/ObservablesContextExtension.cs b/Software/Visual_Studio/Tango.BL/ObservablesContextExtension.cs index b7559cdc3..263574f68 100644 --- a/Software/Visual_Studio/Tango.BL/ObservablesContextExtension.cs +++ b/Software/Visual_Studio/Tango.BL/ObservablesContextExtension.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data.Entity; +using System.Data.Entity.Core.Objects; using System.Data.Entity.Infrastructure; using System.Data.SQLite; using System.IO; @@ -35,7 +36,7 @@ namespace Tango.BL /// public ObservablesContext() { - + } /// @@ -102,6 +103,15 @@ namespace Tango.BL return CreateDefault(address, "Tango", DataSourceType.SQLServer); } + /// + /// Gets the inner object context. + /// + /// + private ObjectContext GetObjectContext() + { + return ((IObjectContextAdapter)this).ObjectContext; + } + /// /// Saves all changes made in this context to the underlying database. /// @@ -110,6 +120,14 @@ namespace Tango.BL /// public override int SaveChanges() { + foreach (var entity in ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified).ToList()) + { + if (entity is IObservableEntity && entity != null) + { + (entity as IObservableEntity).OnBeforeSave(); + } + } + var result = base.SaveChanges(); RaisePendingNotifications(); return result; @@ -129,6 +147,14 @@ namespace Tango.BL /// public override Task SaveChangesAsync(CancellationToken cancellationToken) { + foreach (var entity in ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified).ToList().Select(x => x.Entity).ToList()) + { + if (entity is IObservableEntity && entity != null) + { + (entity as IObservableEntity).OnBeforeSave(); + } + } + var result = base.SaveChangesAsync(cancellationToken); RaisePendingNotifications(); return result; @@ -168,9 +194,6 @@ namespace Tango.BL { IObservableEntity modified = entityEntry.Entity as IObservableEntity; - //Good chance to update "LAST_UPDATED" field! - modified.LastUpdated = DateTime.UtcNow; - foreach (var toNotify in ObservableEntitiesContainer.RegisteredEntities.ToList().Where(x => x.Guid == modified.Guid).ToList()) { _pending_notifications.Add(new ObservableModifiedEventArgs(this, modified, toNotify)); diff --git a/Software/Visual_Studio/Tango.CodeGeneration/Templates/TangoWebClientCodeFile.cshtml b/Software/Visual_Studio/Tango.CodeGeneration/Templates/TangoWebClientCodeFile.cshtml index 7d9ebc075..3918ce928 100644 --- a/Software/Visual_Studio/Tango.CodeGeneration/Templates/TangoWebClientCodeFile.cshtml +++ b/Software/Visual_Studio/Tango.CodeGeneration/Templates/TangoWebClientCodeFile.cshtml @@ -42,6 +42,15 @@ namespace @(model.Namespace) } + /// + /// Initializes a new instance of the class. + /// + /// Other instance. + public @(model.Name)(@(model.Name) cloned) : base(cloned) + { + + } + @foreach (var action in model.Actions) {
diff --git a/Software/Visual_Studio/Tango.Core/ExtensionMethods/ExceptionExtensions.cs b/Software/Visual_Studio/Tango.Core/ExtensionMethods/ExceptionExtensions.cs index af4fc39b5..7b87245b7 100644 --- a/Software/Visual_Studio/Tango.Core/ExtensionMethods/ExceptionExtensions.cs +++ b/Software/Visual_Studio/Tango.Core/ExtensionMethods/ExceptionExtensions.cs @@ -30,6 +30,23 @@ public static class ExceptionExtensions return stringBuilder.ToString(); } + /// + /// Gets the first exception if this is an aggregated exception. + /// + /// The exception. + /// + public static Exception GetFirstIfAggregate(this Exception exception) + { + var ex = exception as AggregateException; + + if (ex != null && ex.InnerExceptions.Count > 0) + { + return ex.InnerExceptions.First(); + } + + return exception; + } + /// /// Flattens the exception message in case it is an aggregated exception. /// diff --git a/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs b/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs index 17ec544c0..923cceacf 100644 --- a/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs +++ b/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs @@ -56,6 +56,7 @@ namespace Tango.DAL.Remote.DB public Nullable SAMPLE_DYE_APPROVE_DATE { get; set; } public int EDITING_STATE { get; set; } public double LENGTH_PERCENTAGE_FACTOR { get; set; } + public bool IS_SYNCHRONIZED { get; set; } public virtual COLOR_CATALOGS COLOR_CATALOGS { get; set; } public virtual COLOR_SPACES COLOR_SPACES { get; set; } diff --git a/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB_RUNS.cs b/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB_RUNS.cs index 24f61f03c..186c82727 100644 --- a/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB_RUNS.cs +++ b/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB_RUNS.cs @@ -24,5 +24,6 @@ namespace Tango.DAL.Remote.DB public int STATUS { get; set; } public double END_POSITION { get; set; } public string FAILED_MESSAGE { get; set; } + public bool IS_SYNCHRONIZED { get; set; } } } diff --git a/Software/Visual_Studio/Tango.DAL.Remote/DB/MACHINES_EVENTS.cs b/Software/Visual_Studio/Tango.DAL.Remote/DB/MACHINES_EVENTS.cs index d83f123a2..c555a1ce3 100644 --- a/Software/Visual_Studio/Tango.DAL.Remote/DB/MACHINES_EVENTS.cs +++ b/Software/Visual_Studio/Tango.DAL.Remote/DB/MACHINES_EVENTS.cs @@ -23,6 +23,7 @@ namespace Tango.DAL.Remote.DB public string USER_GUID { get; set; } public System.DateTime DATE_TIME { get; set; } public string DESCRIPTION { get; set; } + public bool IS_SYNCHRONIZED { get; set; } public virtual EVENT_TYPES EVENT_TYPES { get; set; } public virtual MACHINE MACHINE { get; set; } diff --git a/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx b/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx index 8c8b66a5c..1a27648e2 100644 --- a/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx +++ b/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx @@ -593,6 +593,7 @@ + @@ -634,6 +635,7 @@ + @@ -745,6 +747,7 @@ + @@ -3648,6 +3651,7 @@ + @@ -3689,6 +3693,7 @@ + @@ -3828,6 +3833,7 @@ + @@ -5924,6 +5930,7 @@ + @@ -5940,6 +5947,7 @@ + @@ -6085,6 +6093,7 @@ + diff --git a/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx.diagram b/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx.diagram index 2feb68642..a3618fb5f 100644 --- a/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx.diagram +++ b/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx.diagram @@ -5,80 +5,80 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/Tango.Settings/SettingsManager.cs b/Software/Visual_Studio/Tango.Settings/SettingsManager.cs index 88a3f59e1..d106ebaca 100644 --- a/Software/Visual_Studio/Tango.Settings/SettingsManager.cs +++ b/Software/Visual_Studio/Tango.Settings/SettingsManager.cs @@ -93,7 +93,6 @@ namespace Tango.Settings { try { - _logManager.Log("Retrieving settings for " + typeof(T).Name); EnsureLoaded(); } catch (Exception ex) diff --git a/Software/Visual_Studio/Tango.Transport/Web/WebTransportClient.cs b/Software/Visual_Studio/Tango.Transport/Web/WebTransportClient.cs index ed2e69468..e838a6a05 100644 --- a/Software/Visual_Studio/Tango.Transport/Web/WebTransportClient.cs +++ b/Software/Visual_Studio/Tango.Transport/Web/WebTransportClient.cs @@ -21,6 +21,12 @@ namespace Tango.Transport.Web public string AuthenticationToken { get; set; } + public TimeSpan RequestTimeout + { + get { return _httpClient.Timeout; } + set { _httpClient.Timeout = value; } + } + static WebTransportClient() { _settings = new JsonSerializerSettings() @@ -109,6 +115,7 @@ namespace Tango.Transport.Web if (type != null) { exception = Activator.CreateInstance(type, new object[] { exceptionMessage + "\n" + stackTrace }) as Exception; + } else { diff --git a/Software/Visual_Studio/Tango.Web/Controllers/TangoController.cs b/Software/Visual_Studio/Tango.Web/Controllers/TangoController.cs index d82fcb5f2..3fd3d469e 100644 --- a/Software/Visual_Studio/Tango.Web/Controllers/TangoController.cs +++ b/Software/Visual_Studio/Tango.Web/Controllers/TangoController.cs @@ -70,7 +70,7 @@ namespace Tango.Web.Controllers code = HttpStatusCode.Unauthorized; } - var httpException = new HttpResponseException(Request.CreateErrorResponse(code, ex.FlattenMessage())); + var httpException = new HttpResponseException(Request.CreateErrorResponse(code, ex.FlattenMessage(), ex)); #if DEBUG throw httpException; diff --git a/Software/Visual_Studio/Tango.Web/TangoWebClient.cs b/Software/Visual_Studio/Tango.Web/TangoWebClient.cs index 1d2f9fc8e..7c3c6ad2a 100644 --- a/Software/Visual_Studio/Tango.Web/TangoWebClient.cs +++ b/Software/Visual_Studio/Tango.Web/TangoWebClient.cs @@ -37,6 +37,12 @@ namespace Tango.Web public WebToken WebToken { get; private set; } public bool IsAuthenticated { get; private set; } + public TimeSpan RequestTimeout + { + get { return _client.RequestTimeout; } + set { _client.RequestTimeout = value; } + } + public TangoWebClient(DeploymentSlot environment, String controller) { _client = new WebTransportClient(); @@ -57,6 +63,24 @@ namespace Tango.Web Token = token; } + public TangoWebClient(TangoWebClient cloned) + { + _client = new WebTransportClient(); + Environment = cloned.Environment; + Address = cloned.Address; + Controller = cloned.Controller; + IsAuthenticated = cloned.IsAuthenticated; + RequestTimeout = cloned.RequestTimeout; + WebToken = cloned.WebToken; + _lastLoginRequest = cloned._lastLoginRequest; + Token = cloned.Token; + } + + public T Clone() where T : TangoWebClient + { + return Activator.CreateInstance(typeof(T), new object[] { this }) as T; + } + public async Task Login(TLoginRequest request) { var response = await _client.PostJson(GetActionAddress("Login"), request); @@ -85,7 +109,7 @@ namespace Tango.Web var response = await _client.PostJson(GetActionAddress(action), request); return response; } - catch (TokenExpiredException) + catch (TokenExpiredException ex) { try { @@ -98,7 +122,7 @@ namespace Tango.Web throw; } } - catch + catch (Exception ex) { throw; } diff --git a/Software/Visual_Studio/Tango.sln b/Software/Visual_Studio/Tango.sln index 8dc160543..b089332d6 100644 --- a/Software/Visual_Studio/Tango.sln +++ b/Software/Visual_Studio/Tango.sln @@ -325,6 +325,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.PPC.Packages.SamplePr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.PPC.Packages.SamplePostPackage", "PPC\UpdatePackages\Tango.PPC.Packages.SamplePostPackage\Tango.PPC.Packages.SamplePostPackage.csproj", "{DA391B02-AE28-4EA1-A80F-D0F4C8029FFA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.PPC.SchemaSynchronizer.CLI", "PPC\Tango.PPC.SchemaSynchronizer.CLI\Tango.PPC.SchemaSynchronizer.CLI.csproj", "{F3746F2B-E4AE-498B-9D42-74F95D992460}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AppVeyor|Any CPU = AppVeyor|Any CPU @@ -5777,6 +5779,46 @@ Global {DA391B02-AE28-4EA1-A80F-D0F4C8029FFA}.Release|x64.Build.0 = Release|Any CPU {DA391B02-AE28-4EA1-A80F-D0F4C8029FFA}.Release|x86.ActiveCfg = Release|Any CPU {DA391B02-AE28-4EA1-A80F-D0F4C8029FFA}.Release|x86.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|Any CPU.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|Any CPU.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|ARM.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|ARM.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|ARM64.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|ARM64.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|x64.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|x64.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|x86.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.AppVeyor|x86.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|ARM.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|ARM.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|ARM64.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|x64.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|x64.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|x86.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Debug|x86.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|Any CPU.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|Any CPU.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|ARM.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|ARM.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|ARM64.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|ARM64.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|x64.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|x64.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|x86.ActiveCfg = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.DefaultBuild|x86.Build.0 = Debug|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|Any CPU.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|ARM.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|ARM.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|ARM64.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|ARM64.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|x64.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|x64.Build.0 = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|x86.ActiveCfg = Release|Any CPU + {F3746F2B-E4AE-498B-9D42-74F95D992460}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5884,6 +5926,7 @@ Global {D74893F2-9E39-4C72-BDD4-937404E1FC37} = {5F6BBAA8-EAD0-4B18-97E5-55B4F56DD760} {2CD12594-3522-4658-A65F-190EE58B6AFA} = {E728CBD9-1AF4-4814-A218-E4BD26E7EDEA} {DA391B02-AE28-4EA1-A80F-D0F4C8029FFA} = {E728CBD9-1AF4-4814-A218-E4BD26E7EDEA} + {F3746F2B-E4AE-498B-9D42-74F95D992460} = {C81ED1A3-D18C-4D80-A8F5-061994A14A60} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution BuildVersion_UseGlobalSettings = False diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/MachineStudioController.cs b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/MachineStudioController.cs index 378fc3eea..da5ce16f7 100644 --- a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/MachineStudioController.cs +++ b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/MachineStudioController.cs @@ -285,7 +285,16 @@ namespace Tango.MachineService.Controllers [HttpPost] public LoginResponse Login(LoginRequest request) { - var authResult = _ad_manager.ValidateUserCredentials(request.Email, request.Password); + AuthenticationResult authResult = null; + + try + { + authResult = _ad_manager.ValidateUserCredentials(request.Email, request.Password); + } + catch (Exception ex) + { + throw new AuthenticationException(ex.FlattenMessage()); + } if (!_ad_manager.CanUserAccessCurrentEnvironment(request.Email)) { diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/PPCController.cs b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/PPCController.cs index 0d9a2993b..b2a1da980 100644 --- a/Software/Visual_Studio/Web/Tango.MachineService/Controllers/PPCController.cs +++ b/Software/Visual_Studio/Web/Tango.MachineService/Controllers/PPCController.cs @@ -398,12 +398,9 @@ namespace Tango.MachineService.Controllers Type = DataSourceType.SQLServer, }; - var machine_version = db.MachineVersions.SingleOrDefault(x => x.Guid == machine.MachineVersionGuid); - var latest_machine_version = db.TangoVersions.Where(x => x.MachineVersionGuid == machine_version.Guid).ToList().OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault(); - TangoUpdate tangoUpdate = new TangoUpdate(); - tangoUpdate.ApplicationVersion = latest_machine_version.Version; - tangoUpdate.FirmwareVersion = latest_machine_version.FirmwareVersion; + tangoUpdate.ApplicationVersion = request.ApplicationVersion; + tangoUpdate.FirmwareVersion = request.FirmwareVersion; tangoUpdate.MachineGuid = machine.Guid; tangoUpdate.StartDate = DateTime.UtcNow; tangoUpdate.UpdateStatus = TangoUpdateStatuses.DatabaseStarted; @@ -423,6 +420,276 @@ namespace Tango.MachineService.Controllers #endregion + #region Synchronization + + [HttpPost] + [JwtTokenFilter] + public UploadMachineDataResponse UploadMachineData(UploadMachineDataRequest request) + { + UploadMachineDataResponse response = new UploadMachineDataResponse(); + response.NotifyCompletedToken = Guid.NewGuid().ToString(); + + using (ObservablesContext db = ObservablesContextHelper.CreateContext()) + { + var machine = db.Machines.SingleOrDefault(x => x.Guid == RequestToken.Object.MachineGuid); + + if (machine == null) + { + throw new AuthenticationException("The specified machine could not be found."); + } + + TangoUpdate tangoUpdate = new TangoUpdate(); + tangoUpdate.ApplicationVersion = request.ApplicationVersion; + tangoUpdate.FirmwareVersion = request.FirmwareVersion; + tangoUpdate.MachineGuid = machine.Guid; + tangoUpdate.StartDate = DateTime.UtcNow; + tangoUpdate.UpdateStatus = TangoUpdateStatuses.SynchronizationStarted; + db.TangoUpdates.Add(tangoUpdate); + db.SaveChanges(); + + _pendingUpdates.Add(new PPCPendingUpdate() + { + Token = response.NotifyCompletedToken, + TangoUpdateGuid = tangoUpdate.Guid, + }); + } + + User machineUser = null; + + try + { + using (ObservablesContext db = ObservablesContextHelper.CreateContext()) + { + var machine = db.Machines.SingleOrDefault(x => x.Guid == RequestToken.Object.MachineGuid); + + if (machine == null) + { + throw new AuthenticationException("The specified machine could not be found."); + } + + machineUser = db.Users.Include(x => x.Contact).SingleOrDefault(x => x.Contact.FirstName == machine.Name); + + if (machineUser == null) + { + //No machine user then create one. + machineUser = new User(); + machineUser.Email = machine.SerialNumber + "@twine-s.com"; + machineUser.Password = machine.SerialNumber; + machineUser.OrganizationGuid = machine.OrganizationGuid; + machineUser.Contact = new Contact(); + machineUser.Contact.Email = machineUser.Email; + machineUser.Contact.FirstName = machine.Name; + machineUser.Contact.LastName = machine.Name; + machineUser.Contact.FullName = machine.Name; + machineUser.Address = new Address(); + + db.Users.Add(machineUser); + db.SaveChanges(); + } + } + + //Insert/Replace Jobs. + foreach (var dto in request.Jobs) + { + using (ObservablesContext db = ObservablesContextHelper.CreateContext()) + { + try + { + var job = dto.ToObservable(); + + job.ID = 0; + job.UserGuid = machineUser.Guid; + job.CustomerGuid = null; + job.IsSynchronized = true; + + var existingJob = db.Jobs.SingleOrDefault(x => x.Guid == job.Guid); + + if (existingJob == null) + { + db.Jobs.Add(job); + db.SaveChanges(); + } + else if (job.LastUpdated > existingJob.LastUpdated) + { + existingJob.Delete(db); + db.Jobs.Add(job); + db.SaveChanges(); + } + } + catch (Exception ex) + { + response.FailedJobs.Add(new SynchronizationFailedEntity() + { + Guid = dto.Guid, + Reason = ex.FlattenMessage(), + }); + } + } + } + + //Insert JobRuns. + foreach (var dto in request.JobRuns) + { + using (ObservablesContext db = ObservablesContextHelper.CreateContext()) + { + try + { + var run = dto.ToObservable(); + run.ID = 0; + run.IsSynchronized = true; + + if (db.JobRuns.SingleOrDefault(x => x.Guid == run.Guid) == null) + { + db.JobRuns.Add(run); + db.SaveChanges(); + } + } + catch (Exception ex) + { + response.FailedJobRuns.Add(new SynchronizationFailedEntity() + { + Guid = dto.Guid, + Reason = ex.FlattenMessage(), + }); + } + } + } + + //Insert MachineEvents. + foreach (var dto in request.MachineEvents) + { + using (ObservablesContext db = ObservablesContextHelper.CreateContext()) + { + try + { + var ev = dto.ToObservable(); + ev.ID = 0; + ev.UserGuid = machineUser.Guid; + ev.IsSynchronized = true; + + if (db.MachinesEvents.SingleOrDefault(x => x.Guid == ev.Guid) == null) + { + db.MachinesEvents.Add(ev); + db.SaveChanges(); + } + } + catch (Exception ex) + { + response.FailedMachineEvents.Add(new SynchronizationFailedEntity() + { + Guid = dto.Guid, + Reason = ex.FlattenMessage(), + }); + } + } + } + } + catch (Exception ex) + { + NotifyUpdateCompleted(new MachineUpdateCompletedRequest() + { + Status = TangoUpdateStatuses.SynchronizationFailed, + FailedReason = ex.FlattenMessage(), + FailedLog = ex.FlattenException(), + Token = response.NotifyCompletedToken, + }); + throw ex; + } + + return response; + } + + [HttpPost] + [JwtTokenFilter] + public DownloadMachineDataResponse DownloadMachineData(DownloadMachineDataRequest request) + { + DownloadMachineDataResponse response = new DownloadMachineDataResponse(); + + using (ObservablesContext db = ObservablesContextHelper.CreateContext()) + { + var machine = db.Machines.SingleOrDefault(x => x.Guid == RequestToken.Object.MachineGuid); + + if (machine == null) + { + throw new AuthenticationException("The specified machine could not be found."); + } + + //Send Jobs + if (request.RequestJobs) + { + var jobs = new JobsCollectionBuilder(db).Set(x => x.MachineGuid == machine.Guid && !x.IsSynchronized).WithSegments().WithBrushStops().Query(x => x.Take(request.MaxJobs).OrderByDescending(z => z.LastUpdated)).BuildList(); + + foreach (var job in jobs) + { + JobDTO dto = JobDTO.FromObservable(job); + response.Jobs.Add(dto); + } + } + + //Send Job Runs + if (request.RequestJobRuns) + { + var jobRuns = db.JobRuns.Where(x => x.MachineGuid == machine.Guid && !x.IsSynchronized).Take(request.MaxJobRuns).OrderByDescending(x => x.LastUpdated).ToList(); + + foreach (var jobRun in jobRuns) + { + JobRunDTO dto = JobRunDTO.FromObservable(jobRun); + response.JobRuns.Add(dto); + } + } + + //Send Machine Events + if (request.RequestMachineEvents) + { + var machineEvents = db.MachinesEvents.Where(x => x.MachineGuid == machine.Guid && !x.IsSynchronized).Take(request.MaxMachinesEvents).OrderByDescending(x => x.LastUpdated).ToList(); + + foreach (var machineEvent in machineEvents) + { + MachinesEventDTO dto = MachinesEventDTO.FromObservable(machineEvent); + response.MachineEvents.Add(dto); + } + } + } + + return response; + } + + [HttpPost] + [JwtTokenFilter] + public NotifyMachineDataDownloadCompletedResponse NotifyMachineDataDownloadCompleted(NotifyMachineDataDownloadCompletedRequest request) + { + var response = new NotifyMachineDataDownloadCompletedResponse(); + + using (ObservablesContext db = ObservablesContextHelper.CreateContext()) + { + var machine = db.Machines.SingleOrDefault(x => x.Guid == RequestToken.Object.MachineGuid); + + if (machine == null) + { + throw new AuthenticationException("The specified machine could not be found."); + } + + if (request.SynchronizedJobs.Count > 0) + { + db.Database.ExecuteSqlCommand($"UPDATE JOBS SET IS_SYNCHRONIZED = 1 WHERE GUID IN ({String.Join(",", request.SynchronizedJobs.Select(x => "'" + x + "'"))});"); + } + + if (request.SynchronizedJobRuns.Count > 0) + { + db.Database.ExecuteSqlCommand($"UPDATE JOB_RUNS SET IS_SYNCHRONIZED = 1 WHERE GUID IN ({String.Join(",", request.SynchronizedJobRuns.Select(x => "'" + x + "'"))});"); + } + + if (request.SynchronizedMachineEvents.Count > 0) + { + db.Database.ExecuteSqlCommand($"UPDATE MACHINES_EVENTS SET IS_SYNCHRONIZED = 1 WHERE GUID IN ({String.Join(",", request.SynchronizedMachineEvents.Select(x => "'" + x + "'"))});"); + } + } + + return response; + } + + #endregion + #region Version Upload [HttpPost] diff --git a/Software/Visual_Studio/Web/Tango.MachineService/Web.config b/Software/Visual_Studio/Web/Tango.MachineService/Web.config index 97a0d511f..658439cc0 100644 --- a/Software/Visual_Studio/Web/Tango.MachineService/Web.config +++ b/Software/Visual_Studio/Web/Tango.MachineService/Web.config @@ -25,7 +25,7 @@ - + @@ -62,6 +62,11 @@ + + + + + -- cgit v1.3.1 From 76a028f95dc2bdb2aba4a58ca298268be32a5879 Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Sat, 14 Dec 2019 19:04:41 +0200 Subject: Implemented sites module on MS. Implemented site selection on Machine Designer. --- Software/DB/TCC/TCC.mdf | Bin 8388608 -> 8388608 bytes Software/DB/TCC/TCC_log.ldf | Bin 8388608 -> 8388608 bytes Software/DB/Tango.mdf | Bin 75497472 -> 75497472 bytes Software/DB/Tango_log.ldf | Bin 22675456 -> 22675456 bytes .../ViewModels/MainViewVM.cs | 21 ++- .../Views/MachineSettingsView.xaml | 3 + .../Modules/Tango.MachineStudio.Sites/App.xaml | 12 ++ .../Contracts/IMainView.cs | 20 +++ .../Images/machine_site.png | Bin 0 -> 207547 bytes .../Tango.MachineStudio.Sites/Models/SiteModel.cs | 18 ++ .../Tango.MachineStudio.Sites.csproj | 32 +++- .../ViewModels/MainViewVM.cs | 198 ++++++++++++++++++++- .../ViewModels/SiteDetailsViewVM.cs | 153 ++++++++++++++++ .../Tango.MachineStudio.Sites/Views/MainView.xaml | 6 +- .../Views/MainView.xaml.cs | 10 +- .../Views/SiteDetailsView.xaml | 170 ++++++++++++++++++ .../Views/SiteDetailsView.xaml.cs | 28 +++ .../Tango.MachineStudio.Sites/Views/SitesView.xaml | 76 ++++++++ .../Views/SitesView.xaml.cs | 28 +++ .../Modules/Tango.MachineStudio.Sites/app.config | 10 ++ .../Tango.MachineStudio.Sites/packages.config | 1 + Software/Visual_Studio/Tango.BL/Entities/Site.cs | 36 ++++ 22 files changed, 816 insertions(+), 6 deletions(-) create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/App.xaml create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Contracts/IMainView.cs create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Images/machine_site.png create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Models/SiteModel.cs create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/ViewModels/SiteDetailsViewVM.cs create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SiteDetailsView.xaml create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SiteDetailsView.xaml.cs create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SitesView.xaml create mode 100644 Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SitesView.xaml.cs (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels') diff --git a/Software/DB/TCC/TCC.mdf b/Software/DB/TCC/TCC.mdf index 37ed9b842..f77c2e733 100644 Binary files a/Software/DB/TCC/TCC.mdf and b/Software/DB/TCC/TCC.mdf differ diff --git a/Software/DB/TCC/TCC_log.ldf b/Software/DB/TCC/TCC_log.ldf index 616875f6b..048d3ccb0 100644 Binary files a/Software/DB/TCC/TCC_log.ldf and b/Software/DB/TCC/TCC_log.ldf differ diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf index 5650526a0..6af55afc8 100644 Binary files a/Software/DB/Tango.mdf and b/Software/DB/Tango.mdf differ diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf index 6a5c87335..33e76f9de 100644 Binary files a/Software/DB/Tango_log.ldf and b/Software/DB/Tango_log.ldf differ diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs index 768f93de1..fd31cd950 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/ViewModels/MainViewVM.cs @@ -115,6 +115,21 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels set { _selectedSpool = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } + private Site _selectedSite; + public Site SelectedSite + { + get { return _selectedSite; } + set { _selectedSite = value; RaisePropertyChangedAuto(); } + } + + private List _sites; + public List Sites + { + get { return _sites; } + set { _sites = value; RaisePropertyChangedAuto(); } + } + + private ColorCalibrationViewVM _colorCalibrationViewVM; public ColorCalibrationViewVM ColorCalibrationViewVM { @@ -474,6 +489,10 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels } } + Sites = await ActiveMachineAdapter.Context.Sites.ToListAsync(); + Sites.Insert(0, new Site() { Name = "NONE", ID = -1 }); + SelectedSite = Sites.SingleOrDefault(x => x.Guid == ActiveMachine.SiteGuid); + ColorCalibrationViewVM = new ColorCalibrationViewVM(_notification, ActiveMachine, _activeMachineAdapter.Context) { Rmls = ActiveMachineAdapter.Rmls, @@ -487,7 +506,6 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels await MachineUpdatesViewVM.Init(ActiveMachine, ActiveMachineAdapter.Context); TupViewVM.Init(ActiveMachine); - ActiveMachine.Configuration.HardwareVersionChanged += Configuration_HardwareVersionChanged; View.NavigateTo(MachineDesignerNavigationView.MachineDetailsView); @@ -651,6 +669,7 @@ namespace Tango.MachineStudio.MachineDesigner.ViewModels ActiveMachine.ConfigurationGuid = ActiveMachine.Configuration.Guid; ActiveMachine.LastUpdated = DateTime.UtcNow; ActiveMachine.ProductionDate = DateTime.UtcNow; + ActiveMachine.SiteGuid = SelectedSite == null ? null : (SelectedSite.ID == -1 ? null : SelectedSite.Guid); ColorCalibrationViewVM.Save(); diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineSettingsView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineSettingsView.xaml index b6c1ba974..0a0c1ecef 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineSettingsView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.MachineDesigner/Views/MachineSettingsView.xaml @@ -48,6 +48,9 @@ Organization + Site + + OS Key diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/App.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/App.xaml new file mode 100644 index 000000000..01a064b05 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/App.xaml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Contracts/IMainView.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Contracts/IMainView.cs new file mode 100644 index 000000000..67f57b28c --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Contracts/IMainView.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.SharedUI; + +namespace Tango.MachineStudio.Sites.Contracts +{ + public enum SitesNavigationView + { + SitesView, + SiteDetailsView, + } + + public interface IMainView : IView + { + void NavigateTo(SitesNavigationView view); + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Images/machine_site.png b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Images/machine_site.png new file mode 100644 index 000000000..9b22e53b0 Binary files /dev/null and b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Images/machine_site.png differ diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Models/SiteModel.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Models/SiteModel.cs new file mode 100644 index 000000000..007f8a3ad --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Models/SiteModel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.BL.Entities; +using Tango.SharedUI.Components; + +namespace Tango.MachineStudio.Sites.Models +{ + public class SiteModel : Site + { + public String Organization { get; set; } + public int MachineCount { get; set; } + public int CatalogCount { get; set; } + public int ThreadCount { get; set; } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Tango.MachineStudio.Sites.csproj b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Tango.MachineStudio.Sites.csproj index 95f71baa8..ea9162ce2 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Tango.MachineStudio.Sites.csproj +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Tango.MachineStudio.Sites.csproj @@ -32,6 +32,12 @@ 4 + + ..\..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + + + ..\..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + ..\..\..\packages\Google.Protobuf.3.4.1\lib\net45\Google.Protobuf.dll @@ -48,6 +54,7 @@ ..\..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + ..\..\..\packages\MahApps.Metro.1.5.0\lib\net45\System.Windows.Interactivity.dll @@ -66,8 +73,11 @@ + + + MainView.xaml @@ -75,10 +85,28 @@ GlobalVersionInfo.cs + + SiteDetailsView.xaml + + + SitesView.xaml + + + MSBuild:Compile + Designer + Designer MSBuild:Compile + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + @@ -105,7 +133,9 @@ Settings.Designer.cs - + + + {f441feee-322a-4943-b566-110e12fd3b72} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/ViewModels/MainViewVM.cs index d0be162dd..486c8b46f 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/ViewModels/MainViewVM.cs @@ -4,15 +4,209 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.BL; +using Tango.BL.Entities; +using Tango.Core.Commands; +using Tango.Core.Threading; using Tango.MachineStudio.Common; +using Tango.MachineStudio.Common.Notifications; +using Tango.MachineStudio.Sites.Contracts; +using Tango.MachineStudio.Sites.Models; namespace Tango.MachineStudio.Sites.ViewModels { - public class MainViewVM : StudioViewModel + public class MainViewVM : StudioViewModel { + private ObservablesContext _db; + private INotificationProvider _notification; + private ActionTimer _filter_timer; + + private List _sites; + public List Sites + { + get { return _sites; } + set { _sites = value; RaisePropertyChangedAuto(); } + } + + private SiteModel _selectedSite; + public SiteModel SelectedSite + { + get { return _selectedSite; } + set { _selectedSite = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private String _filter; + public String Filter + { + get { return _filter; } + set { _filter = value; RaisePropertyChangedAuto(); OnFilterChanged(); } + } + + private SiteDetailsViewVM _siteDetailsViewVM; + public SiteDetailsViewVM SiteDetailsViewVM + { + get { return _siteDetailsViewVM; } + set { _siteDetailsViewVM = value; RaisePropertyChangedAuto(); } + } + + public RelayCommand AddSiteCommand { get; set; } + + public RelayCommand RemoveSiteCommand { get; set; } + + public RelayCommand ManageSiteCommand { get; set; } + + public RelayCommand BackToSitesCommand { get; set; } + + public MainViewVM(INotificationProvider notificationProvider) + { + _notification = notificationProvider; + _filter_timer = new ActionTimer(TimeSpan.FromMilliseconds(500)); + + ManageSiteCommand = new RelayCommand(() => LoadSelectedSite(), () => SelectedSite != null); + BackToSitesCommand = new RelayCommand(() => { View.NavigateTo(SitesNavigationView.SitesView); }); + AddSiteCommand = new RelayCommand(AddNewSite); + RemoveSiteCommand = new RelayCommand(RemoveSelectedSite, () => SelectedSite != null); + } + + private async void RemoveSelectedSite() + { + if (!_notification.ShowQuestion("Are you sure you wish to remove the selected site?")) return; + + try + { + using (_notification.PushTaskItem("Removing site...")) + { + IsFree = false; + await Task.Factory.StartNew(() => + { + var site = _db.Sites.SingleOrDefault(x => x.Guid == SelectedSite.Guid); + site.Delete(_db); + _db.SaveChanges(); + Sites.Remove(SelectedSite); + LoadSites(); + }); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error removing site."); + _notification.ShowError($"Error removing site.\n{ex.FlattenMessage()}"); + } + finally + { + IsFree = true; + } + } + + private async void AddNewSite() + { + try + { + String name = _notification.ShowTextInput("Enter site name", "name"); + if (String.IsNullOrWhiteSpace(name)) return; + + using (_notification.PushTaskItem("Creating site...")) + { + IsFree = false; + SiteDetailsViewVM = new SiteDetailsViewVM(); + SiteDetailsViewVM.Saved += SiteDetailsViewVM_Saved; + await SiteDetailsViewVM.Init(SelectedSite?.Guid, _notification, true, name); + View.NavigateTo(SitesNavigationView.SiteDetailsView); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error creating site."); + _notification.ShowError($"Error creating site.\n{ex.FlattenMessage()}"); + } + finally + { + IsFree = true; + } + } + + private async void LoadSelectedSite() + { + try + { + using (_notification.PushTaskItem("Loading site details...")) + { + IsFree = false; + SiteDetailsViewVM = new SiteDetailsViewVM(); + SiteDetailsViewVM.Saved += SiteDetailsViewVM_Saved; + await SiteDetailsViewVM.Init(SelectedSite.Guid, _notification, false); + View.NavigateTo(SitesNavigationView.SiteDetailsView); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error loading site details."); + _notification.ShowError($"Error loading site details.\n{ex.FlattenMessage()}"); + } + finally + { + IsFree = true; + } + } + + private void SiteDetailsViewVM_Saved(object sender, EventArgs e) + { + OnFilterChanged(); + View.NavigateTo(SitesNavigationView.SitesView); + } + + private void OnFilterChanged() + { + if (Filter != null) + { + _filter_timer.ResetReplace(() => + { + try + { + LoadSites(); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error loading sites list."); + } + }); + } + } + + private void LoadSites() + { + using (_notification.PushTaskItem("Loading sites...")) + { + Sites = (from site in _db.Sites + join organization in _db.Organizations on site.OrganizationGuid equals organization.Guid + join sites_rmls in _db.SitesRmls on site.Guid equals sites_rmls.SiteGuid into rmls + join sites_catalogs in _db.SitesCatalogs on site.Guid equals sites_catalogs.SiteGuid into catalogs + join sites_machines in _db.Machines on site.Guid equals sites_machines.SiteGuid into machines + where Filter == null || Filter == "" || site.Name.ToLower().StartsWith(Filter.ToLower()) || organization.Name.ToLower().StartsWith(Filter.ToLower()) + select new + { + Site = site, + OrganizationName = organization.Name, + ThreadCount = rmls.Count(x => x.SiteGuid == site.Guid), + CatalogCount = catalogs.Count(x => x.SiteGuid == site.Guid), + MachineCount = machines.Count(x => x.SiteGuid == site.Guid) + }).Distinct().ToList().DistinctBy(x => x.Site.Guid).Select(x => new SiteModel() + { + Guid = x.Site.Guid, + ID = x.Site.ID, + Name = x.Site.Name, + Description = x.Site.Description, + ThreadCount = x.ThreadCount, + CatalogCount = x.CatalogCount, + MachineCount = x.MachineCount, + Organization = x.OrganizationName, + }).ToList(); + } + } + public override void OnApplicationReady() { - + _db = ObservablesContext.CreateDefault(); } } } diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/ViewModels/SiteDetailsViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/ViewModels/SiteDetailsViewVM.cs new file mode 100644 index 000000000..0e4dcbb47 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/ViewModels/SiteDetailsViewVM.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.BL; +using Tango.BL.Entities; +using Tango.Core.Commands; +using Tango.SharedUI; +using Tango.SharedUI.Components; +using System.Data.Entity; +using Tango.MachineStudio.Common.Notifications; + +namespace Tango.MachineStudio.Sites.ViewModels +{ + public class SiteDetailsViewVM : ViewModel + { + private ObservablesContext _db; + private INotificationProvider _notification; + + public event EventHandler Saved; + + private Site _site; + public Site Site + { + get { return _site; } + set { _site = value; RaisePropertyChangedAuto(); } + } + + private List _organizations; + public List Organizations + { + get { return _organizations; } + set { _organizations = value; RaisePropertyChangedAuto(); } + } + + private Organization _selectedOrganization; + public Organization SelectedOrganization + { + get { return _selectedOrganization; } + set { _selectedOrganization = value; RaisePropertyChangedAuto(); } + } + + private SelectedObjectCollection _rmls; + public SelectedObjectCollection Rmls + { + get { return _rmls; } + set { _rmls = value; RaisePropertyChangedAuto(); } + } + + private SelectedObjectCollection _catalogs; + public SelectedObjectCollection Catalogs + { + get { return _catalogs; } + set { _catalogs = value; RaisePropertyChangedAuto(); } + } + + private List _machines; + public List Machines + { + get { return _machines; } + set { _machines = value; RaisePropertyChangedAuto(); } + } + + public RelayCommand SaveCommand { get; set; } + + public SiteDetailsViewVM() + { + SaveCommand = new RelayCommand(Save, () => IsFree); + } + + public async Task Init(String siteGuid, INotificationProvider notification, bool isNew, string newSiteName = null) + { + _notification = notification; + + _db = ObservablesContext.CreateDefault(); + Organizations = await _db.Organizations.ToListAsync(); + + if (isNew) + { + Site = new Site(); + Site.Name = newSiteName; + Site.Description = "My site description"; + _db.Sites.Add(Site); + } + else + { + Site = await _db.Sites.SingleOrDefaultAsync(x => x.Guid == siteGuid); + } + + Machines = await _db.Machines.Where(x => x.SiteGuid == Site.Guid).Include(x => x.Organization).ToListAsync(); + + var site_rmls = await _db.SitesRmls.Where(x => x.SiteGuid == Site.Guid).ToListAsync(); + var site_catalogs = await _db.SitesCatalogs.Where(x => x.SiteGuid == Site.Guid).ToListAsync(); + + var rmls = await _db.Rmls.OrderBy(x => x.Name).ToListAsync(); + var catalogs = await _db.ColorCatalogs.OrderBy(x => x.Name).ToListAsync(); + + Rmls = new SelectedObjectCollection(rmls.ToObservableCollection(), rmls.Where(rml => site_rmls.Exists(siteRml => siteRml.RmlGuid == rml.Guid)).ToObservableCollection()); + Catalogs = new SelectedObjectCollection(catalogs.ToObservableCollection(), catalogs.Where(catalog => site_catalogs.Exists(siteCatalog => siteCatalog.CatalogGuid == catalog.Guid)).ToObservableCollection()); + + SelectedOrganization = Organizations.SingleOrDefault(x => x.Guid == Site.OrganizationGuid); + } + + private async void Save() + { + try + { + Site.OrganizationGuid = SelectedOrganization?.Guid; + + if (!Site.Validate(_db)) + { + _notification.ShowError(String.Join("\n", Site.ValidationErrors)); + return; + } + + IsFree = false; + + using (_notification.PushTaskItem("Saving site details...")) + { + var site_rmls = await _db.SitesRmls.Where(x => x.SiteGuid == Site.Guid).ToListAsync(); + var site_catalogs = await _db.SitesCatalogs.Where(x => x.SiteGuid == Site.Guid).ToListAsync(); + + _db.SitesRmls.RemoveRange(site_rmls); + _db.SitesCatalogs.RemoveRange(site_catalogs); + + foreach (var selectedRml in Rmls.SynchedSource) + { + _db.SitesRmls.Add(new SitesRml() { SiteGuid = Site.Guid, RmlGuid = selectedRml.Guid }); + } + + foreach (var selectedCatalog in Catalogs.SynchedSource) + { + _db.SitesCatalogs.Add(new SitesCatalog() { SiteGuid = Site.Guid, CatalogGuid = selectedCatalog.Guid }); + } + + await _db.SaveChangesAsync(); + } + _db.Dispose(); + Saved?.Invoke(this, new EventArgs()); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error saving site details."); + _notification.ShowError($"Error saving site details.\n{ex.FlattenMessage()}"); + } + finally + { + IsFree = true; + } + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/MainView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/MainView.xaml index 80f9ae151..4ca82c4f7 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/MainView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/MainView.xaml @@ -7,9 +7,13 @@ xmlns:vm="clr-namespace:Tango.MachineStudio.Sites.ViewModels" xmlns:global="clr-namespace:Tango.MachineStudio.Sites" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI" mc:Ignorable="d" d:DesignHeight="1080" d:DesignWidth="1920" Background="Transparent" d:DataContext="{d:DesignInstance Type=vm:MainViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.MainViewVM}"> - Comming soon... + + + + diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/MainView.xaml.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/MainView.xaml.cs index 75ced5ae2..c4f9995ce 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/MainView.xaml.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/MainView.xaml.cs @@ -12,17 +12,25 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using Tango.Core.DI; +using Tango.MachineStudio.Sites.Contracts; namespace Tango.MachineStudio.Sites.Views { /// /// Interaction logic for MainView.xaml /// - public partial class MainView : UserControl + public partial class MainView : UserControl, IMainView { public MainView() { InitializeComponent(); + TangoIOC.Default.Register(this); + } + + public void NavigateTo(SitesNavigationView view) + { + navigationControl.NavigateTo(view.ToString()); } } } diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SiteDetailsView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SiteDetailsView.xaml new file mode 100644 index 000000000..b3496a5e2 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SiteDetailsView.xaml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROPERTIES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SITE RML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SiteDetailsView.xaml.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SiteDetailsView.xaml.cs new file mode 100644 index 000000000..03e9a2f75 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SiteDetailsView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +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.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.MachineStudio.Sites.Views +{ + /// + /// Interaction logic for SiteDetailsView.xaml + /// + public partial class SiteDetailsView : UserControl + { + public SiteDetailsView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SitesView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SitesView.xaml new file mode 100644 index 000000000..437003a8a --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SitesView.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SitesView.xaml.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SitesView.xaml.cs new file mode 100644 index 000000000..e6a4a6fe6 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/Views/SitesView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +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.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.MachineStudio.Sites.Views +{ + /// + /// Interaction logic for SitesView.xaml + /// + public partial class SitesView : UserControl + { + public SitesView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/app.config b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/app.config index 97a204217..7b82e5f7c 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/app.config +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/app.config @@ -1,5 +1,9 @@  + + +
+ @@ -72,4 +76,10 @@ + + + + + + \ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/packages.config b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/packages.config index fd88f4804..e57143046 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/packages.config +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Sites/packages.config @@ -1,5 +1,6 @@  + diff --git a/Software/Visual_Studio/Tango.BL/Entities/Site.cs b/Software/Visual_Studio/Tango.BL/Entities/Site.cs index d88a00567..6e95aa39d 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/Site.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/Site.cs @@ -8,5 +8,41 @@ namespace Tango.BL.Entities { public class Site : SiteBase { + public override void Delete(ObservablesContext context) + { + base.Delete(context); + + var site_rmls = context.SitesRmls.Where(x => x.SiteGuid == Guid).ToList(); + var site_catalogs = context.SitesCatalogs.Where(x => x.SiteGuid == Guid).ToList(); + context.SitesRmls.RemoveRange(site_rmls); + context.SitesCatalogs.RemoveRange(site_catalogs); + context.Machines.Where(x => x.SiteGuid == Guid).ToList().ForEach(x => x.SiteGuid = null); + context.Sites.Remove(this); + } + + protected override void OnValidating(ObservablesContext context) + { + base.OnValidating(context); + + if (String.IsNullOrWhiteSpace(Name)) + { + InsertError(nameof(Name), "Site name is required"); + } + + if (Name != null && Name.Length > 100) + { + InsertError(nameof(Name), "The site name exceeds the maximum allowed characters."); + } + + if (Description != null && Description.Length > 200) + { + InsertError(nameof(Description), "The site description exceeds the maximum allowed characters."); + } + + if (OrganizationGuid == null) + { + InsertError(nameof(OrganizationGuid), "Site organization is required."); + } + } } } -- cgit v1.3.1