diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2019-12-11 20:57:30 +0200 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2019-12-11 20:57:30 +0200 |
| commit | 621230afe9d9040536b43241e63117c9bb34beaa (patch) | |
| tree | c9f3a3793372a1be6a7e73cdf633e2dae40c21c9 /Software/Visual_Studio | |
| parent | 3f069bb4a5303b2c732ba1263229f62526acc693 (diff) | |
| download | Tango-621230afe9d9040536b43241e63117c9bb34beaa.tar.gz Tango-621230afe9d9040536b43241e63117c9bb34beaa.zip | |
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.
Diffstat (limited to 'Software/Visual_Studio')
87 files changed, 2444 insertions, 126 deletions
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 @@ <DataTrigger Binding="{Binding IsDataBase}" Value="True"> <Setter Property="Kind" Value="Database"></Setter> </DataTrigger> + <DataTrigger Binding="{Binding IsSynchronization}" Value="True"> + <Setter Property="Kind" Value="Sync"></Setter> + </DataTrigger> <DataTrigger Binding="{Binding IsStarted}" Value="True"> <Setter Property="Foreground" Value="{StaticResource OrangeBrush}"></Setter> </DataTrigger> 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 @@ <converters:DateTimeUTCToShortDateTimeConverter x:Key="DateTimeUTCToShortDateTimeConverter" /> <converters:StringToOneLineConverter x:Key="StringToOneLineConverter" /> </UserControl.Resources> - + <Grid Margin="20" DataContext="{Binding MachineUpdatesViewVM}"> - <DockPanel> + <DockPanel IsEnabled="{Binding IsFree}"> <Grid DockPanel.Dock="Bottom"> </Grid> @@ -34,11 +34,16 @@ <local:MachineView Margin="30 80 0 0" DataContext="{Binding Machine}" IsHitTestVisible="False" VerticalAlignment="Top" Height="241" /> <DockPanel Grid.Column="1" Margin="50 100 50 50"> - <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Height="50"> - <CheckBox IsChecked="{Binding DisplayMachineSetups}">Machine Setups</CheckBox> - <CheckBox IsChecked="{Binding DisplayApplicationUpdates}" Margin="30 0">Software Updates</CheckBox> - <CheckBox IsChecked="{Binding DisplayDatabaseUpdates}">Database Updates</CheckBox> - </StackPanel> + <Grid DockPanel.Dock="Bottom" Height="50"> + <StackPanel Orientation="Horizontal"> + <CheckBox IsChecked="{Binding DisplayMachineSetups}">Machine Setups</CheckBox> + <CheckBox IsChecked="{Binding DisplayApplicationUpdates}" Margin="30 0">Software Updates</CheckBox> + <CheckBox IsChecked="{Binding DisplayDatabaseUpdates}">Database Updates</CheckBox> + <CheckBox IsChecked="{Binding DisplaySynchronizations}" Margin="30 0 0 0">Synchronizations</CheckBox> + </StackPanel> + + <Button Command="{Binding RefreshCommand}" HorizontalAlignment="Right" Width="150" Style="{StaticResource MaterialDesignFlatButton}">REFRESH</Button> + </Grid> <Grid> <DataGrid Margin="0 0 0 10" SelectionUnit="FullRow" BorderBrush="{StaticResource borderBrush }" BorderThickness="1" Background="{StaticResource TransparentBackgroundBrush}" AlternatingRowBackground="{StaticResource Transparent200}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True" ItemsSource="{Binding Updates}" SelectedItem="{Binding SelectedUpdate}"> <DataGrid.CellStyle> @@ -66,6 +71,9 @@ <DataTrigger Binding="{Binding IsDataBase}" Value="True"> <Setter Property="Kind" Value="Database"></Setter> </DataTrigger> + <DataTrigger Binding="{Binding IsSynchronization}" Value="True"> + <Setter Property="Kind" Value="Sync"></Setter> + </DataTrigger> <DataTrigger Binding="{Binding IsStarted}" Value="True"> <Setter Property="Foreground" Value="{StaticResource OrangeBrush}"></Setter> </DataTrigger> 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 /// <param name="message">The message.</param> 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())); } /// <summary> @@ -147,7 +147,7 @@ namespace Tango.MachineStudio.Common.EventLogging /// <param name="e">The <see cref="RequestFailedEventArgs"/> instance containing the event data.</param> 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())); } /// <summary> @@ -157,7 +157,7 @@ namespace Tango.MachineStudio.Common.EventLogging /// <param name="message">The message.</param> 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())); } /// <summary> 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 @@ -41,6 +41,15 @@ namespace Tango.MachineStudio.Common.Web } /// <summary> + /// Initializes a new instance of the <see cref="MachineStudioWebClientBase"/> class. + /// </summary> + /// <param name="cloned">Other instance.</param> + public MachineStudioWebClientBase(MachineStudioWebClientBase cloned) : base(cloned) + { + + } + + /// <summary> /// Executes the CheckForUpdates action and returns Tango.MachineStudio.Common.Web.CheckForUpdatesResponse. /// </summary> /// <returns></returns> 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<MachineStudioSettings>(); - _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<MachineStudioWebClient>(); - TangoIOC.Default.Register<MachineStudioWebClient, MachineStudioWebClient>(new MachineStudioWebClient()); + + if (App.StartupArgs.Contains("-webDebug")) + { + TangoIOC.Default.Register<MachineStudioWebClient, MachineStudioWebClient>(new MachineStudioWebClient("http://localhost:1111", null)); + } + else + { + TangoIOC.Default.Register<MachineStudioWebClient, MachineStudioWebClient>(new MachineStudioWebClient()); + } + TangoIOC.Default.Register<IDispatcherProvider, DefaultDispatcherProvider>(new DefaultDispatcherProvider(Application.Current.Dispatcher)); TangoIOC.Default.Register<INotificationProvider, DefaultNotificationProvider>(); TangoIOC.Default.Register<IAuthenticationProvider, DefaultAuthenticationProvider>(); @@ -84,7 +94,7 @@ namespace Tango.MachineStudio.UI TangoIOC.Default.Register<ISpeechProvider, DefaultSpeechProvider>(); TangoIOC.Default.Register<IFirmwareUpgrader, DefaultFirmwareUpgrader>(); TangoIOC.Default.Register<TeamFoundationServiceExtendedClient>(new TeamFoundationServiceExtendedClient("https://twinetfs.visualstudio.com", String.Empty, "szzfokrceo4rhd4eqi5qpmxn3pa5iwl3q7tlqd36l2m7smz2ynoa")); - + TangoIOC.Default.Register<MainViewVM>(); TangoIOC.Default.Register<LoadingViewVM>(); 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 Binary files differnew file mode 100644 index 000000000..4e46ee447 --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync-job.png 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 Binary files differnew file mode 100644 index 000000000..46059c5c0 --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/sync.png 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 +{ + /// <summary> + /// Represents a simple text message notification item which can be inserted into the application notifications panel. + /// </summary> + /// <seealso cref="Tango.PPC.Common.Notifications.NotificationItem" /> + public class NewSynchronizardJobsNotificationItem : NotificationItem + { + /// <summary> + /// Initializes a new instance of the <see cref="UpdateAvailableNotificationItem"/> class. + /// </summary> + public NewSynchronizardJobsNotificationItem() + { + CanClose = true; + } + + /// <summary> + /// Gets or sets the view type. + /// </summary> + 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 @@ +<UserControl x:Class="Tango.PPC.Jobs.NotificationItems.NewSynchronizardJobsNotificationItemView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:touch="clr-namespace:Tango.Touch.Controls;assembly=Tango.Touch" + xmlns:local="clr-namespace:Tango.PPC.Jobs.NotificationItems" + xmlns:common="clr-namespace:Tango.PPC.Common.Converters" + mc:Ignorable="d" + x:Name="MessageNotificationItemControl" + d:DesignHeight="60" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=local:NewSynchronizardJobsNotificationItem, IsDesignTimeCreatable=False}" MinHeight="90" Height="90" MaxHeight="150" Background="White"> + + <Grid> + <Border BorderThickness="0 0 0 2" BorderBrush="{StaticResource TangoPrimaryAccentBrush}" Padding="15"> + <DockPanel> + <Image Source="../Images/sync-job.png" MaxHeight="50" /> + + <Grid> + <TextBlock Margin="20 0 0 0" VerticalAlignment="Center"> + New job definitions were synchronized with your machine. Tap to refresh your job list. + </TextBlock> + </Grid> + </DockPanel> + </Border> + </Grid> +</UserControl> 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 +{ + /// <summary> + /// Represents the <see cref="UpdateAvailableNotificationItemView"/> view. + /// </summary> + /// <seealso cref="System.Windows.Controls.UserControl" /> + /// <seealso cref="System.Windows.Markup.IComponentConnector" /> + 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 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="NotificationItems\NewSynchronizardJobsNotificationItemView.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> <Page Include="Resources\Styles.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> @@ -228,6 +232,10 @@ <Compile Include="NavigationObjects\JobNavigationObject.cs" /> <Compile Include="NavigationObjects\JobSummeryNavigationObject.cs" /> <Compile Include="NavigationObjects\TwineCatalogNavigationObject.cs" /> + <Compile Include="NotificationItems\NewSynchronizardJobsNotificationItem.cs" /> + <Compile Include="NotificationItems\NewSynchronizardJobsNotificationItemView.xaml.cs"> + <DependentUpon>NewSynchronizardJobsNotificationItemView.xaml</DependentUpon> + </Compile> <Compile Include="Properties\AssemblyInfo.cs"> <SubType>Code</SubType> </Compile> @@ -486,10 +494,13 @@ <ItemGroup> <Resource Include="Images\color-picker.png" /> </ItemGroup> + <ItemGroup> + <Resource Include="Images\sync.png" /> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> + <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> </VisualStudio> </ProjectExtensions> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/PPC/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<NewSynchronizardJobsNotificationItem>(); + 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 /// </summary> public RelayCommand DiscardCommand { get; set; } + /// <summary> + /// Gets or sets the synchronize command. + /// </summary> + 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}"> - <Grid Background="{StaticResource TangoMidBackgroundBrush}"> + <Grid Background="{StaticResource TangoMidBackgroundBrush}" IsEnabled="{Binding IsFree}"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="1*"/> @@ -195,6 +195,45 @@ </DockPanel> </StackPanel> </touch:TouchExpander> + + <!--SYNCHRONIZATION--> + <touch:TouchExpander Margin="0 20 0 0" Header="Cloud Synchronization" IsExpanded="True" FontSize="{StaticResource TangoExpanderHeaderFontSize}"> + <StackPanel Margin="10 30 10 10"> + <DockPanel TextElement.FontSize="{StaticResource TangoDefaultFontSize}"> + <StackPanel> + <TextBlock VerticalAlignment="Center">Synchronize Jobs</TextBlock> + <DockPanel Margin="0 5 0 0"> + <touch:TouchIcon VerticalAlignment="Top" Icon="InformationOutline" Foreground="{StaticResource TangoGrayTextBrush}"></touch:TouchIcon> + <TextBlock Margin="10 0 0 0" VerticalAlignment="Top" TextWrapping="Wrap" FontSize="{StaticResource TangoSmallFontSize}" Foreground="{StaticResource TangoGrayTextBrush}"> + Synchronize your jobs with twine's cloud services. + </TextBlock> + </DockPanel> + </StackPanel> + <touch:TouchToggleSlider Margin="0 0 100 0" DockPanel.Dock="Right" Style="{StaticResource TangoToggleButtonGrayAccent}" HorizontalAlignment="Right" Width="90" IsChecked="{Binding SynchronizeJobs}"></touch:TouchToggleSlider> + </DockPanel> + + <DockPanel Margin="0 40 0 0" TextElement.FontSize="{StaticResource TangoDefaultFontSize}"> + <StackPanel> + <TextBlock VerticalAlignment="Center">Synchronize Diagnostics Data</TextBlock> + <DockPanel Margin="0 5 0 0"> + <touch:TouchIcon VerticalAlignment="Top" Icon="InformationOutline" Foreground="{StaticResource TangoGrayTextBrush}"></touch:TouchIcon> + <TextBlock Margin="10 0 0 0" VerticalAlignment="Top" TextWrapping="Wrap" FontSize="{StaticResource TangoSmallFontSize}" Foreground="{StaticResource TangoGrayTextBrush}"> + Help us improve your experience using this system. + </TextBlock> + </DockPanel> + </StackPanel> + <touch:TouchToggleSlider Margin="0 0 100 0" DockPanel.Dock="Right" Style="{StaticResource TangoToggleButtonGrayAccent}" HorizontalAlignment="Right" Width="90" IsChecked="{Binding SynchronizeDiagnostics}"></touch:TouchToggleSlider> + </DockPanel> + + <DockPanel Margin="0 40 0 0"> + <touch:TouchIcon VerticalAlignment="Top" Icon="InformationOutline" Foreground="{StaticResource TangoGrayTextBrush}"></touch:TouchIcon> + <TextBlock Margin="10 0 0 0" VerticalAlignment="Top" TextWrapping="Wrap" FontSize="{StaticResource TangoSmallFontSize}" Foreground="{StaticResource TangoGrayTextBrush}"> + Once enabled, synchronization occurres automatically in the background. you can choose to synchronize right now. + </TextBlock> + </DockPanel> + <touch:TouchButton Command="{Binding SynchronizeCommand}" IsEnabled="{Binding MachineDataSynchronizer.IsEnabled}" HorizontalAlignment="Left" Margin="25 10 0 0" Style="{StaticResource TangoHollowButton}" FontSize="{StaticResource TangoDefaultFontSize}" Padding="15 10" CornerRadius="22">Synchronize Now</touch:TouchButton> + </StackPanel> + </touch:TouchExpander> </StackPanel> </touch:LightTouchScrollViewer> </Grid> 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 Binary files differnew file mode 100644 index 000000000..46059c5c0 --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Technician/Images/sync.png 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 @@ <Compile Include="ViewModels\LoggingViewVM.cs" /> <Compile Include="ViewModels\MainViewVM.cs" /> <Compile Include="ViewModels\PackagesViewVM.cs" /> + <Compile Include="ViewModels\SynchronizationViewVM.cs" /> <Compile Include="ViewModels\SystemViewVM.cs" /> <Compile Include="Views\CatalogView.xaml.cs"> <DependentUpon>CatalogView.xaml</DependentUpon> </Compile> + <Compile Include="Views\SynchronizationView.xaml.cs"> + <DependentUpon>SynchronizationView.xaml</DependentUpon> + </Compile> <Compile Include="Views\PackagesView.xaml.cs"> <DependentUpon>PackagesView.xaml</DependentUpon> </Compile> @@ -191,6 +195,10 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Views\SynchronizationView.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> <Page Include="Views\PackagesView.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> @@ -234,5 +242,8 @@ <ItemGroup> <Resource Include="Images\packages.png" /> </ItemGroup> + <ItemGroup> + <Resource Include="Images\sync.png" /> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project>
\ 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<DispensersViewVM>(); TangoIOC.Default.Register<SystemViewVM>(); TangoIOC.Default.Register<PackagesViewVM>(); + TangoIOC.Default.Register<SynchronizationViewVM>(); } /// <summary> @@ -88,5 +89,16 @@ namespace Tango.PPC.Technician return TangoIOC.Default.GetInstance<PackagesViewVM>(); } } + + /// <summary> + /// Gets the synchronization view vm. + /// </summary> + public static SynchronizationViewVM SynchronizationViewVM + { + get + { + return TangoIOC.Default.GetInstance<SynchronizationViewVM>(); + } + } } } 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 @@ <Image Source="../Images/logging.png" Width="80" Height="80" /> <StackPanel Margin="10 0 0 0"> <TextBlock FontSize="{StaticResource TangoButtonFontSize}">Logging</TextBlock> - <TextBlock Foreground="{StaticResource TangoGrayTextBrush}"> + <TextBlock Foreground="{StaticResource TangoGrayTextBrush}" TextWrapping="Wrap" Width="580"> Display and investigate issues using application and embedded device logs. </TextBlock> </StackPanel> @@ -62,7 +62,7 @@ <Image Source="../Images/system.png" Width="80" Height="80" /> <StackPanel Margin="10 0 0 0"> <TextBlock FontSize="{StaticResource TangoTitleFontSize}">System</TextBlock> - <TextBlock Foreground="{StaticResource TangoGrayTextBrush}"> + <TextBlock Foreground="{StaticResource TangoGrayTextBrush}" TextWrapping="Wrap" Width="580"> Display system properties, perform system actions, reset, shutdown etc... </TextBlock> </StackPanel> @@ -74,7 +74,7 @@ <Image Source="../Images/dispensers.png" Width="80" Height="80" /> <StackPanel Margin="10 0 0 0"> <TextBlock FontSize="{StaticResource TangoButtonFontSize}">Dispensers</TextBlock> - <TextBlock Foreground="{StaticResource TangoGrayTextBrush}"> + <TextBlock Foreground="{StaticResource TangoGrayTextBrush}" TextWrapping="Wrap" Width="580"> Perform manual dispensers homing priming. </TextBlock> </StackPanel> @@ -86,12 +86,24 @@ <Image Source="../Images/packages.png" Width="80" Height="80" /> <StackPanel Margin="10 0 0 0"> <TextBlock FontSize="{StaticResource TangoButtonFontSize}">Installed Packages</TextBlock> - <TextBlock Foreground="{StaticResource TangoGrayTextBrush}"> + <TextBlock Foreground="{StaticResource TangoGrayTextBrush}" TextWrapping="Wrap" Width="580"> View the history of update packages installation. </TextBlock> </StackPanel> </StackPanel> </touch:TouchButton> + + <touch:TouchButton Command="{Binding NavigationCommand}" CommandParameter="SynchronizationView" Style="{StaticResource ButtonMenu}"> + <StackPanel Orientation="Horizontal" HorizontalAlignment="Left"> + <Image Source="../Images/sync.png" Width="80" Height="80" /> + <StackPanel Margin="10 0 0 0"> + <TextBlock FontSize="{StaticResource TangoButtonFontSize}">Synchronization</TextBlock> + <TextBlock Foreground="{StaticResource TangoGrayTextBrush}" TextWrapping="Wrap" Width="580"> + View the current status and history of synchronization operations to the machine service. + </TextBlock> + </StackPanel> + </StackPanel> + </touch:TouchButton> </StackPanel> </Grid> </Grid> 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 @@ <local:DispensersView/> <local:SystemView/> <local:PackagesView/> + <local:SynchronizationView/> </controls:NavigationControl> </Grid> </UserControl> 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 @@ +<UserControl x:Class="Tango.PPC.Technician.Views.SynchronizationView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:Tango.PPC.Technician.Views" + xmlns:sys="clr-namespace:System;assembly=mscorlib" + xmlns:vm="clr-namespace:Tango.PPC.Technician.ViewModels" + xmlns:touch="clr-namespace:Tango.Touch.Controls;assembly=Tango.Touch" + xmlns:converters="clr-namespace:Tango.PPC.Technician.Converters" + xmlns:global="clr-namespace:Tango.PPC.Technician" + mc:Ignorable="d" + d:DesignHeight="1280" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:SynchronizationViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.SynchronizationViewVM}"> + + <UserControl.Resources> + <converters:LogItemMessageToOneLineConverter x:Key="LogItemMessageToOneLineConverter" /> + </UserControl.Resources> + + <Grid Background="{StaticResource TangoMidBackgroundBrush}"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="1*"/> + </Grid.RowDefinitions> + + <Border Padding="20" Background="{StaticResource TangoPrimaryBackgroundBrush}" BorderThickness="0 0 0 1" BorderBrush="{StaticResource TangoDividerBrush}"> + <Border.Effect> + <DropShadowEffect Color="Silver" ShadowDepth="0" BlurRadius="20" Opacity="1" /> + </Border.Effect> + <TextBlock VerticalAlignment="Center" FontSize="{StaticResource TangoHeaderFontSize}" FontWeight="SemiBold">Synchronization</TextBlock> + </Border> + + <Grid Grid.Row="1"> + + <DockPanel> + <DockPanel DockPanel.Dock="Bottom"> + <DockPanel Margin="20 0 20 20"> + <touch:TouchButton IsEnabled="{Binding MachineDataSynchronizer.IsEnabled}" DockPanel.Dock="Right" Padding="50 20" CornerRadius="30" Command="{Binding SynchronizeCommand}">Synchronize Now</touch:TouchButton> + <TextBlock Foreground="{StaticResource TangoGrayTextBrush}" VerticalAlignment="Center" TextWrapping="Wrap" Margin="0 0 20 0">Synchronization occurres automatically in the background. You can choose to synchronize now.</TextBlock> + </DockPanel> + </DockPanel> + + <Grid Margin="20"> + <touch:TouchSimpleDataGrid Background="{StaticResource TangoPrimaryBackgroundBrush}" AutoGenerateColumns="False" SelectionMode="Single" SelectionUnit="FullRow" BorderThickness="1" BorderBrush="{StaticResource TangoDarkForegroundBrush}" HeadersVisibility="Column" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserResizeRows="False" CanUserResizeColumns="False" CanUserSortColumns="False" IsReadOnly="True" ItemsSource="{Binding MachineDataSynchronizer.StatusHistory}" SelectedItem="{Binding SelectedStatus}" VerticalGridLinesBrush="{x:Null}" HorizontalGridLinesBrush="{StaticResource TangoGrayBrush}" RowHeight="50" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled"> + <DataGrid.Resources> + <Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}" TargetType="{x:Type DataGridColumnHeader}"> + <Setter Property="Background" Value="{StaticResource TangoDarkForegroundBrush}" /> + <Setter Property="Foreground" Value="{StaticResource TangoLightForegroundBrush}" /> + <Setter Property="Padding" Value="5"></Setter> + </Style> + </DataGrid.Resources> + <DataGrid.RowStyle> + <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}"> + <Style.Triggers> + <Trigger Property="IsSelected" Value="True"> + <Setter Property="Background" Value="Transparent"></Setter> + <Setter Property="Foreground" Value="{StaticResource TangoPrimaryAccentBrush}" /> + </Trigger> + <Trigger Property="IsFocused" Value="True"> + <Setter Property="Background" Value="Transparent"></Setter> + <Setter Property="Foreground" Value="{StaticResource TangoPrimaryAccentBrush}" /> + </Trigger> + </Style.Triggers> + </Style> + </DataGrid.RowStyle> + <DataGrid.CellStyle> + <Style TargetType="{x:Type DataGridCell}"> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type DataGridCell}"> + <Grid Background="{TemplateBinding Background}"> + <ContentPresenter VerticalAlignment="Center" /> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + <Style.Triggers> + <Trigger Property="IsSelected" Value="True"> + <Setter Property="Background" Value="Transparent"></Setter> + <Setter Property="Foreground" Value="{StaticResource TangoPrimaryAccentBrush}" /> + </Trigger> + </Style.Triggers> + </Style> + </DataGrid.CellStyle> + <DataGrid.Columns> + <DataGridTemplateColumn Header="" Width="50"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <touch:TouchIcon Width="16"> + <touch:TouchIcon.Style> + <Style TargetType="touch:TouchIcon"> + <Setter Property="Icon" Value="Pause"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding State}" Value="Pending"> + <Setter Property="Icon" Value="Pause"></Setter> + <Setter Property="Foreground" Value="{StaticResource TangoGrayBrush}"></Setter> + </DataTrigger> + <DataTrigger Binding="{Binding State}" Value="Synchronizing"> + <Setter Property="Icon" Value="CloudSync"></Setter> + <Setter Property="Foreground" Value="{StaticResource TangoWarningBrush}"></Setter> + </DataTrigger> + <DataTrigger Binding="{Binding State}" Value="Failed"> + <Setter Property="Icon" Value="Alert"></Setter> + <Setter Property="Foreground" Value="{StaticResource TangoErrorBrush}"></Setter> + </DataTrigger> + <DataTrigger Binding="{Binding State}" Value="Completed"> + <Setter Property="Icon" Value="Check"></Setter> + <Setter Property="Foreground" Value="{StaticResource TangoGreenBrush}"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </touch:TouchIcon.Style> + </touch:TouchIcon> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTextColumn Header="Start Time" Width="120" Binding="{Binding StartDateTime,StringFormat=t}" /> + <DataGridTextColumn Header="Status" Width="120" Binding="{Binding State,Converter={StaticResource EnumToDescriptionConverter}}" /> + <DataGridTextColumn Header="Duration" Width="120" Binding="{Binding Duration,StringFormat='hh\\:mm\\:ss'}" /> + <DataGridTemplateColumn Header="Message" Width="1*"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <TextBlock Text="{Binding Message,Converter={StaticResource StringEllipsisConverter},ConverterParameter='70',Mode=OneWay}"/> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + </DataGrid.Columns> + </touch:TouchSimpleDataGrid> + </Grid> + </DockPanel> + + </Grid> + </Grid> +</UserControl> 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 +{ + /// <summary> + /// Interaction logic for SynchronizationView.xaml + /// </summary> + 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 @@ -96,6 +96,11 @@ namespace Tango.PPC.Common.Application Version Version { get; } /// <summary> + /// Gets the firmware version. + /// </summary> + Version FirmwareVersion { get; } + + /// <summary> /// Gets the application build date. /// </summary> String BuildDate { get; } 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<LogItemBase> _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<MachineSetupResult> 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<PPCSettings>().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 @@ -180,6 +180,26 @@ namespace Tango.PPC.Common public String PreviousApplicationVersion { get; set; } /// <summary> + /// Gets or sets a value indicating whether synchronize jobs with twine server. + /// </summary> + public bool SynchronizeJobs { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether synchronize diagnostics data. + /// </summary> + public bool SynchronizeDiagnostics { get; set; } + + /// <summary> + /// Gets or sets the synchronization interval. + /// </summary> + public TimeSpan SynchronizationInterval { get; set; } + + /// <summary> + /// Gets or sets the known firmware version. + /// </summary> + public String FirmwareVersion { get; set; } + + /// <summary> /// Gets the machine service address. /// </summary> /// <returns></returns> @@ -214,6 +234,10 @@ namespace Tango.PPC.Common SupportedColorSpaces = new List<ColorSpaces>(); SupportedJobTypes = new List<JobTypes>(); 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; } + /// <summary> + /// Gets or sets the machine data synchronizer. + /// </summary> + [TangoInject] + public IMachineDataSynchronizer MachineDataSynchronizer { get; set; } + private PPCSettings _settings; /// <summary> /// 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<LogItemBase> _logs; + private bool _synchronizedOnce; + + [TangoInject(TangoInjectMode.WhenAvailable)] + private IPPCApplicationManager _appManager; + + public event EventHandler<SynchronizationStatusChangedEventArgs> CurrentStatusChanged; + public event EventHandler SynchronizationStarted; + public event EventHandler<SynchronizationEndedEventArgs> SynchronizationEnded; + + public int MaxJobs { get; set; } + public int MaxJobRuns { get; set; } + public int MaxMachinesEvents { get; set; } + + private SynchronizationStatus _currentStatus; + public SynchronizationStatus CurrentStatus + { + get { return _currentStatus; } + private set { _currentStatus = value; RaisePropertyChangedAuto(); } + } + + private SynchronizationStatus _lastStatus; + public SynchronizationStatus LastStatus + { + get { return _lastStatus; } + private set { _lastStatus = value; RaisePropertyChangedAuto(); } + } + + public SynchronizedObservableCollection<SynchronizationStatus> StatusHistory { get; private set; } + + public TimeSpan Interval { get; set; } + + private bool _isEnabled; + public bool IsEnabled + { + get { return _isEnabled; } + set { _isEnabled = value; OnEnableChanged(); RaisePropertyChangedAuto(); } + } + + private bool _isSynchronizing; + public bool IsSynchronizing + { + get { return _isSynchronizing; } + set { _isSynchronizing = value; RaisePropertyChangedAuto(); } + } + + public DefaultMachineDataSynchronizer() + { + StatusHistory = new SynchronizedObservableCollection<SynchronizationStatus>(); + + MaxJobs = 10; + MaxJobRuns = 100; + MaxMachinesEvents = 100; + + var settings = SettingsManager.Default.GetOrCreate<PPCSettings>(); + Interval = settings.SynchronizationInterval; + + _synchTimer = new Timer(Interval.TotalMilliseconds); + _synchTimer.Elapsed += _synchTimer_Elapsed; + _synchTimer.Enabled = true; + + ExecuteNewStatus(TimeSpan.FromMinutes(2)); + LastStatus = CurrentStatus; + } + + public DefaultMachineDataSynchronizer(PPCWebClient ppcWebClient, IMachineProvider machineProvider, IAuthenticationProvider authenticationProvider) : this() + { + _logs = new List<LogItemBase>(); + _machineProvider = machineProvider; + client = new PPCWebClient(ppcWebClient, TimeSpan.FromMinutes(10)); + _authenticationProvider = authenticationProvider; + + LogManager.NewLog += LogManager_NewLog; + } + + private void LogManager_NewLog(object sender, LogItemBase e) + { + if (IsSynchronizing) + { + _logs.Add(e); + } + } + + private String GetLogsStringAndClear() + { + String logsString = String.Join(Environment.NewLine, _logs.ToList().Select(x => x.ToString())); + _logs.Clear(); + return logsString; + } + + private void OnEnableChanged() + { + _synchTimer.Interval = Interval.TotalMilliseconds; + + if (IsEnabled) + { + CurrentStatus.State = SynchronizationState.Pending; + } + else + { + CurrentStatus.State = SynchronizationState.Disabled; + } + } + + private void _synchTimer_Elapsed(object sender, ElapsedEventArgs e) + { + _synchTimer.Interval = Interval.TotalMilliseconds; + + try + { + Synchronize().GetAwaiter().GetResult(); + } + catch { } + } + + private async Task<UploadMachineDataRequest> CreateUploadMachineDataRequest(bool syncJobs, bool syncDiagnostics) + { + UploadMachineDataRequest request = new UploadMachineDataRequest(); + + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + if (syncJobs) + { + LogManager.Log("Checking Jobs..."); + + var jobs = await new JobsCollectionBuilder(db).Set(x => !x.IsSynchronized).WithSegments().WithBrushStops().Query(x => x.Take(MaxJobs).OrderByDescending(z => z.LastUpdated)).BuildListAsync(); + List<JobDTO> dtos = new List<JobDTO>(); + + foreach (var job in jobs) + { + var dto = JobDTO.FromObservable(job); + request.Jobs.Add(dto); + } + } + + if (syncDiagnostics) + { + LogManager.Log("Checking Job Runs..."); + + var jobRuns = await db.JobRuns.Where(x => !x.IsSynchronized).Take(MaxJobRuns).OrderByDescending(x => x.LastUpdated).ToListAsync(); + List<JobRunDTO> dtos = new List<JobRunDTO>(); + + foreach (var jobRun in jobRuns) + { + var dto = JobRunDTO.FromObservable(jobRun); + request.JobRuns.Add(dto); + } + } + + if (syncDiagnostics) + { + LogManager.Log("Checking Events..."); + + var machineEvents = await db.MachinesEvents.Where(x => !x.IsSynchronized).Take(MaxMachinesEvents).OrderByDescending(x => x.LastUpdated).ToListAsync(); + List<MachinesEventDTO> dtos = new List<MachinesEventDTO>(); + + foreach (var machineEvent in machineEvents) + { + machineEvent.IsSynchronized = true; + var dto = MachinesEventDTO.FromObservable(machineEvent); + request.MachineEvents.Add(dto); + } + } + } + + 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<DownloadMachineDataResponse> 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<NotifyMachineDataDownloadCompletedRequest> InsertReplaceMachineData(DownloadMachineDataResponse response) + { + NotifyMachineDataDownloadCompletedRequest request = new NotifyMachineDataDownloadCompletedRequest(); + + //Insert/Replace Jobs. + if (response.Jobs.Count > 0) + { + LogManager.Log("Inserting/Replacing Jobs..."); + } + foreach (var dto in response.Jobs) + { + using (ObservablesContext db = ObservablesContext.CreateDefault()) + { + try + { + var job = dto.ToObservable(); + + job.ID = 0; + job.UserGuid = _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<PPCSettings>(); + + 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<SynchronizationEndedEventArgs> SynchronizationEnded; + event EventHandler<SynchronizationStatusChangedEventArgs> CurrentStatusChanged; + int MaxJobs { get; set; } + int MaxJobRuns { get; set; } + int MaxMachinesEvents { get; set; } + SynchronizationStatus CurrentStatus { get; } + SynchronizationStatus LastStatus { get; } + SynchronizedObservableCollection<SynchronizationStatus> StatusHistory { get; } + TimeSpan Interval { get; set; } + bool IsEnabled { get; set; } + bool IsSynchronizing { get; } + Task Synchronize(); + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationEndedEventArgs.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationEndedEventArgs.cs new file mode 100644 index 000000000..4b8040e95 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationEndedEventArgs.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Common.Synchronization +{ + public class SynchronizationEndedEventArgs : EventArgs + { + public int NewChangedJobs { get; set; } + public int NewJobRuns { get; set; } + public int NewMachineEvents { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationState.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationState.cs new file mode 100644 index 000000000..5797f449f --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationState.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Common.Synchronization +{ + public enum SynchronizationState + { + [Description("Pending...")] + Pending, + [Description("Synchronizing...")] + Synchronizing, + [Description("Failed")] + Failed, + [Description("Completed")] + Completed, + [Description("Disabled")] + Disabled + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatus.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatus.cs new file mode 100644 index 000000000..5b1d5d1d2 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatus.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core; + +namespace Tango.PPC.Common.Synchronization +{ + public class SynchronizationStatus : ExtendedObject + { + private SynchronizationState _state; + public SynchronizationState State + { + get { return _state; } + set { _state = value; RaisePropertyChangedAuto(); } + } + + private String _message; + public String Message + { + get { return _message; } + set { _message = value; RaisePropertyChangedAuto(); } + } + + private String _errorReason; + public String ErrorReason + { + get { return _errorReason; } + set { _errorReason = value; RaisePropertyChangedAuto(); } + } + + private TimeSpan? _duration; + public TimeSpan? Duration + { + get { return _duration; } + set { _duration = value; RaisePropertyChangedAuto(); } + } + + private DateTime _startDateTime; + public DateTime StartDateTime + { + get { return _startDateTime; } + set { _startDateTime = value; RaisePropertyChangedAuto(); } + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatusChangedEventArgs.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatusChangedEventArgs.cs new file mode 100644 index 000000000..1f0a9a27f --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Synchronization/SynchronizationStatusChangedEventArgs.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Common.Synchronization +{ + public class SynchronizationStatusChangedEventArgs : EventArgs + { + public SynchronizationStatus Status { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/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 @@ <Compile Include="MachineSetup\MachineSetupManager.cs" /> <Compile Include="MachineSetup\MachineSetupProgress.cs" /> <Compile Include="MachineSetup\MachineSetupResult.cs" /> + <Compile Include="Synchronization\DefaultMachineDataSynchronizer.cs" /> + <Compile Include="Synchronization\IMachineDataSynchronizer.cs" /> + <Compile Include="Synchronization\SynchronizationEndedEventArgs.cs" /> + <Compile Include="Synchronization\SynchronizationState.cs" /> + <Compile Include="Synchronization\SynchronizationStatus.cs" /> + <Compile Include="Synchronization\SynchronizationStatusChangedEventArgs.cs" /> <Compile Include="UpdatePackages\DefaultPackageRunner.cs" /> <Compile Include="UpdatePackages\IPackageRunner.cs" /> <Compile Include="UpdatePackages\IPPCPackage.cs" /> @@ -177,6 +183,8 @@ <Compile Include="Web\CheckForUpdateRequest.cs" /> <Compile Include="Web\CheckForUpdateResponse.cs" /> <Compile Include="MachineUpdate\DbCompareResult.cs" /> + <Compile Include="Web\NotifyMachineDataDownloadCompletedResponse.cs" /> + <Compile Include="Web\NotifyMachineDataDownloadCompletedRequest.cs" /> <Compile Include="Web\DownloadUpdateRequest.cs" /> <Compile Include="Web\MachineUpdateCompletedResponse.cs" /> <Compile Include="Web\DownloadUpdateResponse.cs" /> @@ -192,6 +200,7 @@ <Compile Include="MachineUpdate\MachineUpdateResult.cs" /> <Compile Include="Web\PPCWebClient.cs" /> <Compile Include="Web\PPCWebClientBase.cs" /> + <Compile Include="Web\SynchronizationFailedEntity.cs" /> <Compile Include="Web\UpdateDBRequest.cs" /> <Compile Include="Web\UpdateDBResponse.cs" /> <Compile Include="Messages\JobRemovedMessage.cs" /> @@ -226,8 +235,12 @@ <Compile Include="Web\LatestVersionRequest.cs" /> <Compile Include="Web\LatestVersionResponse.cs" /> <Compile Include="Web\UpdatedEntity.cs" /> + <Compile Include="Web\DownloadMachineDataRequest.cs" /> + <Compile Include="Web\DownloadMachineDataResponse.cs" /> + <Compile Include="Web\UploadMachineDataResponse.cs" /> <Compile Include="Web\UploadCompletedResponse.cs" /> <Compile Include="Web\UploadCompletedRequest.cs" /> + <Compile Include="Web\UploadMachineDataRequest.cs" /> <Compile Include="Web\UploadVersionRequest.cs" /> <Compile Include="Web\UploadVersionResponse.cs" /> <Compile Include="UWF\DefaultUnifiedWriteFilterManager.cs" /> @@ -406,7 +419,7 @@ </Target> <ProjectExtensions> <VisualStudio> - <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" /> + <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" /> </VisualStudio> </ProjectExtensions> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/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<JobDTO> Jobs { get; set; } + public List<JobRunDTO> JobRuns { get; set; } + public List<MachinesEventDTO> MachineEvents { get; set; } + + public DownloadMachineDataResponse() + { + Jobs = new List<JobDTO>(); + JobRuns = new List<JobRunDTO>(); + MachineEvents = new List<MachinesEventDTO>(); + } + } +} 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<String> SynchronizedJobs { get; set; } + public List<String> SynchronizedJobRuns { get; set; } + public List<String> SynchronizedMachineEvents { get; set; } + + public NotifyMachineDataDownloadCompletedRequest() + { + SynchronizedJobs = new List<string>(); + SynchronizedJobRuns = new List<string>(); + SynchronizedMachineEvents = new List<string>(); + } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedResponse.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedResponse.cs new file mode 100644 index 000000000..6d5769885 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/NotifyMachineDataDownloadCompletedResponse.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using Tango.BL.DTO; +using Tango.Transport.Web; + +namespace Tango.PPC.Common.Web +{ + public class NotifyMachineDataDownloadCompletedResponse : WebResponseMessage + { + + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs index 52c9fdef3..318512fbb 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClient.cs @@ -30,5 +30,10 @@ namespace Tango.PPC.Common.Web public PPCWebClient(string address, string token) : base(address, token) { } + + public PPCWebClient(PPCWebClient other, TimeSpan requestTimeout) : base(other) + { + RequestTimeout = requestTimeout; + } } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/PPCWebClientBase.cs index 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 @@ -41,6 +41,15 @@ namespace Tango.PPC.Common.Web } /// <summary> + /// Initializes a new instance of the <see cref="PPCWebClientBase"/> class. + /// </summary> + /// <param name="cloned">Other instance.</param> + public PPCWebClientBase(PPCWebClientBase cloned) : base(cloned) + { + + } + + /// <summary> /// Executes the MachineSetup action and returns Tango.PPC.Common.Web.MachineSetupResponse. /// </summary> /// <returns></returns> @@ -86,6 +95,33 @@ namespace Tango.PPC.Common.Web } /// <summary> + /// Executes the UploadMachineData action and returns Tango.PPC.Common.Web.UploadMachineDataResponse. + /// </summary> + /// <returns></returns> + public Task<Tango.PPC.Common.Web.UploadMachineDataResponse> UploadMachineData(Tango.PPC.Common.Web.UploadMachineDataRequest request) + { + return Post<Tango.PPC.Common.Web.UploadMachineDataRequest, Tango.PPC.Common.Web.UploadMachineDataResponse>("UploadMachineData", request); + } + + /// <summary> + /// Executes the DownloadMachineData action and returns Tango.PPC.Common.Web.DownloadMachineDataResponse. + /// </summary> + /// <returns></returns> + public Task<Tango.PPC.Common.Web.DownloadMachineDataResponse> DownloadMachineData(Tango.PPC.Common.Web.DownloadMachineDataRequest request) + { + return Post<Tango.PPC.Common.Web.DownloadMachineDataRequest, Tango.PPC.Common.Web.DownloadMachineDataResponse>("DownloadMachineData", request); + } + + /// <summary> + /// Executes the NotifyMachineDataDownloadCompleted action and returns Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedResponse. + /// </summary> + /// <returns></returns> + public Task<Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedResponse> NotifyMachineDataDownloadCompleted(Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedRequest request) + { + return Post<Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedRequest, Tango.PPC.Common.Web.NotifyMachineDataDownloadCompletedResponse>("NotifyMachineDataDownloadCompleted", request); + } + + /// <summary> /// Executes the GetLatestVersion action and returns Tango.PPC.Common.Web.LatestVersionResponse. /// </summary> /// <returns></returns> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/SynchronizationFailedEntity.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/SynchronizationFailedEntity.cs new file mode 100644 index 000000000..c50044cbe --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/SynchronizationFailedEntity.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.PPC.Common.Web +{ + public class SynchronizationFailedEntity + { + public String Guid { get; set; } + public String Reason { get; set; } + } +} diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Web/UpdateDBRequest.cs index f3b4ccb34..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<JobDTO> Jobs { get; set; } + public List<JobRunDTO> JobRuns { get; set; } + public List<MachinesEventDTO> MachineEvents { get; set; } + public String ApplicationVersion { get; set; } + public String FirmwareVersion { get; set; } + + public UploadMachineDataRequest() + { + Jobs = new List<JobDTO>(); + JobRuns = new List<JobRunDTO>(); + MachineEvents = new List<MachinesEventDTO>(); + } + } +} 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<SynchronizationFailedEntity> FailedJobs { get; set; } + public List<SynchronizationFailedEntity> FailedJobRuns { get; set; } + public List<SynchronizationFailedEntity> FailedMachineEvents { get; set; } + public String NotifyCompletedToken { get; set; } + + public UploadMachineDataResponse() + { + FailedJobs = new List<SynchronizationFailedEntity>(); + FailedJobRuns = new List<SynchronizationFailedEntity>(); + FailedMachineEvents = new List<SynchronizationFailedEntity>(); + } + } +} 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 @@ +<?xml version="1.0" encoding="utf-8" ?> +<configuration> + <startup> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> + </startup> +</configuration>
\ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{F3746F2B-E4AE-498B-9D42-74F95D992460}</ProjectGuid> + <OutputType>Exe</OutputType> + <RootNamespace>Tango.PPC.SchemaSynchronizer.CLI</RootNamespace> + <AssemblyName>Tango.PPC.SchemaSynchronizer.CLI</AssemblyName> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Program.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="App.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\Tango.Core\Tango.Core.csproj"> + <Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project> + <Name>Tango.Core</Name> + </ProjectReference> + <ProjectReference Include="..\..\Tango.SQLExaminer\Tango.SQLExaminer.csproj"> + <Project>{e1e66ed9-597d-45fa-8048-de90a6930484}</Project> + <Name>Tango.SQLExaminer</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project>
\ 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; @@ -147,15 +149,27 @@ namespace Tango.PPC.UI.PPCApplication } /// <summary> + /// Gets the firmware version. + /// </summary> + public Version FirmwareVersion + { + get + { + return Version.Parse(SettingsManager.Default.GetOrCreate<PPCSettings>().FirmwareVersion); + } + } + + /// <summary> /// Initializes a new instance of the <see cref="DefaultPPCApplicationManager"/> class. /// </summary> - 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<PPCWebClient>(); TangoIOC.Default.Unregister<IBackupManager>(); TangoIOC.Default.Unregister<IPackageRunner>(); + TangoIOC.Default.Unregister<IMachineDataSynchronizer>(); + + if (App.StartupArgs.Contains("-webDebug")) + { + TangoIOC.Default.Register<PPCWebClient, PPCWebClient>(new PPCWebClient("http://localhost:1111", null)); + } + else + { + TangoIOC.Default.Register<PPCWebClient, PPCWebClient>(new PPCWebClient()); + } - TangoIOC.Default.Register<PPCWebClient, PPCWebClient>(new PPCWebClient()); TangoIOC.Default.Register<IDispatcherProvider, DefaultDispatcherProvider>(new DefaultDispatcherProvider(Application.Current.Dispatcher)); TangoIOC.Default.Register<INotificationProvider, DefaultNotificationProvider>(); TangoIOC.Default.Register<IAuthenticationProvider, DefaultAuthenticationProvider>(); @@ -84,6 +95,7 @@ namespace Tango.PPC.UI TangoIOC.Default.Register<INavigationManager, DefaultNavigationManager>(); TangoIOC.Default.Register<IMachineProvider, DefaultMachineProvider>(); TangoIOC.Default.Register<IEventLogger, DefaultEventLogger>(); + TangoIOC.Default.Register<IMachineDataSynchronizer, DefaultMachineDataSynchronizer>(); TangoIOC.Default.Register<IPPCApplicationManager, DefaultPPCApplicationManager>(); TangoIOC.Default.Register<ExternalBridgeScanner, ExternalBridgeScanner>(); TangoIOC.Default.Register<IDiagnosticsFrameProvider, DefaultDiagnosticsFrameProvider>(); 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<KeyValuePair<int, Action>> _steps; private List<KeyValuePair<int, Func<IQueryable<T>, IQueryable<T>>>> _querySteps; private bool _entity_set; + private Func<IQueryable<T>, IQueryable<T>> _appendQuery; protected IEnumerable<T> 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<T>, IQueryable<T>> 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<T> BuildList() + { + if (!_entity_set) + { + throw new InvalidOperationException("Could not build entity. Entity was not set."); + } + + CommitSteps(); + + return Entities.ToList(); + } + public Task<SynchronizedObservableCollection<T>> BuildAsync() { return Task.Factory.StartNew<SynchronizedObservableCollection<T>>(() => @@ -111,5 +141,13 @@ namespace Tango.BL.Builders return Build(); }); } + + public Task<List<T>> BuildListAsync() + { + return Task.Factory.StartNew<List<T>>(() => + { + 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<SegmentDTO> Segments { get; set; } + public JobDTO() + { + Segments = new List<SegmentDTO>(); + } } } 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; } + /// <summary> + /// is synchronized + /// </summary> + 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; } + /// <summary> + /// is synchronized + /// </summary> + 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; } + /// <summary> + /// is synchronized + /// </summary> + 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<BrushStopDTO> BrushStops { get; set; } + public SegmentDTO() + { + BrushStops = new List<BrushStopDTO>(); + } } } 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<Double> LengthPercentageFactorChanged; + public event EventHandler<Boolean> IsSynchronizedChanged; + public event EventHandler<ColorCatalog> ColorCatalogChanged; public event EventHandler<ColorSpace> ColorSpaceChanged; @@ -993,6 +995,33 @@ namespace Tango.BL.Entities } } + protected Boolean _issynchronized; + + /// <summary> + /// Gets or sets the jobbase is synchronized. + /// </summary> + + [Column("IS_SYNCHRONIZED")] + + public Boolean IsSynchronized + { + get + { + return _issynchronized; + } + + set + { + if (_issynchronized != value) + { + _issynchronized = value; + + OnIsSynchronizedChanged(value); + + } + } + } + protected ColorCatalog _colorcatalog; /// <summary> @@ -1500,6 +1529,15 @@ namespace Tango.BL.Entities } /// <summary> + /// Called when the IsSynchronized has changed. + /// </summary> + protected virtual void OnIsSynchronizedChanged(Boolean issynchronized) + { + IsSynchronizedChanged?.Invoke(this, issynchronized); + RaisePropertyChanged(nameof(IsSynchronized)); + } + + /// <summary> /// Called when the ColorCatalog has changed. /// </summary> protected virtual void OnColorCatalogChanged(ColorCatalog colorcatalog) 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<String> FailedMessageChanged; + public event EventHandler<Boolean> IsSynchronizedChanged; + protected String _machineguid; /// <summary> @@ -228,6 +230,33 @@ namespace Tango.BL.Entities } } + protected Boolean _issynchronized; + + /// <summary> + /// Gets or sets the jobrunbase is synchronized. + /// </summary> + + [Column("IS_SYNCHRONIZED")] + + public Boolean IsSynchronized + { + get + { + return _issynchronized; + } + + set + { + if (_issynchronized != value) + { + _issynchronized = value; + + OnIsSynchronizedChanged(value); + + } + } + } + /// <summary> /// Called when the StartDate has changed. /// </summary> @@ -274,6 +303,15 @@ namespace Tango.BL.Entities } /// <summary> + /// Called when the IsSynchronized has changed. + /// </summary> + protected virtual void OnIsSynchronizedChanged(Boolean issynchronized) + { + IsSynchronizedChanged?.Invoke(this, issynchronized); + RaisePropertyChanged(nameof(IsSynchronized)); + } + + /// <summary> /// Initializes a new instance of the <see cref="JobRunBase" /> class. /// </summary> public JobRunBase() : base() 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<String> DescriptionChanged; + public event EventHandler<Boolean> IsSynchronizedChanged; + public event EventHandler<EventType> EventTypeChanged; public event EventHandler<Machine> MachineChanged; @@ -197,6 +199,33 @@ namespace Tango.BL.Entities } } + protected Boolean _issynchronized; + + /// <summary> + /// Gets or sets the machineseventbase is synchronized. + /// </summary> + + [Column("IS_SYNCHRONIZED")] + + public Boolean IsSynchronized + { + get + { + return _issynchronized; + } + + set + { + if (_issynchronized != value) + { + _issynchronized = value; + + OnIsSynchronizedChanged(value); + + } + } + } + protected EventType _eventtype; /// <summary> @@ -321,6 +350,15 @@ namespace Tango.BL.Entities } /// <summary> + /// Called when the IsSynchronized has changed. + /// </summary> + protected virtual void OnIsSynchronizedChanged(Boolean issynchronized) + { + IsSynchronizedChanged?.Invoke(this, issynchronized); + RaisePropertyChanged(nameof(IsSynchronized)); + } + + /// <summary> /// Called when the EventType has changed. /// </summary> protected virtual void OnEventTypeChanged(EventType eventtype) 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 @@ -67,6 +67,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 { get @@ -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 /// </summary> /// <param name="context">The context.</param> void Delete(ObservablesContext context); + + /// <summary> + /// Called when before entity is saved by the context. + /// </summary> + 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 + + /// <summary> + /// Called when before entity is saved by the context. + /// </summary> + public virtual void OnBeforeSave() + { + + } + + #endregion + #region INotify Property Changed /// <summary> 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 /// </summary> public ObservablesContext() { - + } /// <summary> @@ -103,6 +104,15 @@ namespace Tango.BL } /// <summary> + /// Gets the inner object context. + /// </summary> + /// <returns></returns> + private ObjectContext GetObjectContext() + { + return ((IObjectContextAdapter)this).ObjectContext; + } + + /// <summary> /// Saves all changes made in this context to the underlying database. /// </summary> /// <returns> @@ -110,6 +120,14 @@ namespace Tango.BL /// </returns> 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 /// </remarks> public override Task<int> 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) } + /// <summary> + /// Initializes a new instance of the <see cref="@(model.Name)"/> class. + /// </summary> + /// <param name="cloned">Other instance.</param> + public @(model.Name)(@(model.Name) cloned) : base(cloned) + { + + } + @foreach (var action in model.Actions) { <div> 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 @@ -31,6 +31,23 @@ public static class ExceptionExtensions } /// <summary> + /// Gets the first exception if this is an aggregated exception. + /// </summary> + /// <param name="exception">The exception.</param> + /// <returns></returns> + 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; + } + + /// <summary> /// Flattens the exception message in case it is an aggregated exception. /// </summary> /// <param name="exception">The exception.</param> 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<System.DateTime> 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 @@ <Property Name="STATUS" Type="int" Nullable="false" /> <Property Name="END_POSITION" Type="float" Nullable="false" /> <Property Name="FAILED_MESSAGE" Type="nvarchar" MaxLength="300" /> + <Property Name="IS_SYNCHRONIZED" Type="bit" Nullable="false" /> </EntityType> <EntityType Name="JOBS"> <Key> @@ -634,6 +635,7 @@ <Property Name="SAMPLE_DYE_APPROVE_DATE" Type="datetime2" Precision="3" /> <Property Name="EDITING_STATE" Type="int" Nullable="false" /> <Property Name="LENGTH_PERCENTAGE_FACTOR" Type="float" Nullable="false" /> + <Property Name="IS_SYNCHRONIZED" Type="bit" Nullable="false" /> </EntityType> <EntityType Name="LINEAR_MASS_DENSITY_UNITS"> <Key> @@ -745,6 +747,7 @@ <Property Name="USER_GUID" Type="varchar" MaxLength="36" /> <Property Name="DATE_TIME" Type="datetime2" Precision="3" Nullable="false" /> <Property Name="DESCRIPTION" Type="nvarchar(max)" /> + <Property Name="IS_SYNCHRONIZED" Type="bit" Nullable="false" /> </EntityType> <EntityType Name="MEDIA_CONDITIONS"> <Key> @@ -3648,6 +3651,7 @@ <Property Name="STATUS" Type="Int32" Nullable="false" /> <Property Name="END_POSITION" Type="Double" Nullable="false" /> <Property Name="FAILED_MESSAGE" Type="String" MaxLength="300" FixedLength="false" Unicode="true" /> + <Property Name="IS_SYNCHRONIZED" Type="Boolean" Nullable="false" /> </EntityType> <EntityType Name="JOB"> <Key> @@ -3689,6 +3693,7 @@ <Property Name="SAMPLE_DYE_APPROVE_DATE" Type="DateTime" Precision="3" /> <Property Name="EDITING_STATE" Type="Int32" Nullable="false" /> <Property Name="LENGTH_PERCENTAGE_FACTOR" Type="Double" Nullable="false" /> + <Property Name="IS_SYNCHRONIZED" Type="Boolean" Nullable="false" /> <NavigationProperty Name="COLOR_CATALOGS" Relationship="RemoteModel.FK_JOBS_COLOR_CATALOGS" FromRole="JOB" ToRole="COLOR_CATALOGS" /> <NavigationProperty Name="COLOR_SPACES" Relationship="RemoteModel.FK_JOBS_COLOR_SPACES" FromRole="JOB" ToRole="COLOR_SPACES" /> <NavigationProperty Name="CUSTOMER" Relationship="RemoteModel.FK_JOBS_CUSTOMERS" FromRole="JOB" ToRole="CUSTOMER" /> @@ -3828,6 +3833,7 @@ <Property Name="USER_GUID" Type="String" MaxLength="36" FixedLength="false" Unicode="false" /> <Property Name="DATE_TIME" Type="DateTime" Nullable="false" Precision="3" /> <Property Name="DESCRIPTION" Type="String" MaxLength="Max" FixedLength="false" Unicode="true" /> + <Property Name="IS_SYNCHRONIZED" Type="Boolean" Nullable="false" /> <NavigationProperty Name="EVENT_TYPES" Relationship="RemoteModel.FK_MACHINES_EVENTS_EVENTS" FromRole="MACHINES_EVENTS" ToRole="EVENT_TYPES" /> <NavigationProperty Name="MACHINE" Relationship="RemoteModel.FK_MACHINES_EVENTS_MACHINES" FromRole="MACHINES_EVENTS" ToRole="MACHINE" /> <NavigationProperty Name="USER" Relationship="RemoteModel.FK_MACHINES_EVENTS_USERS" FromRole="MACHINES_EVENTS" ToRole="USER" /> @@ -5924,6 +5930,7 @@ <EntitySetMapping Name="JOB_RUNS"> <EntityTypeMapping TypeName="RemoteModel.JOB_RUNS"> <MappingFragment StoreEntitySet="JOB_RUNS"> + <ScalarProperty Name="IS_SYNCHRONIZED" ColumnName="IS_SYNCHRONIZED" /> <ScalarProperty Name="FAILED_MESSAGE" ColumnName="FAILED_MESSAGE" /> <ScalarProperty Name="END_POSITION" ColumnName="END_POSITION" /> <ScalarProperty Name="STATUS" ColumnName="STATUS" /> @@ -5940,6 +5947,7 @@ <EntitySetMapping Name="JOBS"> <EntityTypeMapping TypeName="RemoteModel.JOB"> <MappingFragment StoreEntitySet="JOBS"> + <ScalarProperty Name="IS_SYNCHRONIZED" ColumnName="IS_SYNCHRONIZED" /> <ScalarProperty Name="LENGTH_PERCENTAGE_FACTOR" ColumnName="LENGTH_PERCENTAGE_FACTOR" /> <ScalarProperty Name="EDITING_STATE" ColumnName="EDITING_STATE" /> <ScalarProperty Name="SAMPLE_DYE_APPROVE_DATE" ColumnName="SAMPLE_DYE_APPROVE_DATE" /> @@ -6085,6 +6093,7 @@ <EntitySetMapping Name="MACHINES_EVENTS"> <EntityTypeMapping TypeName="RemoteModel.MACHINES_EVENTS"> <MappingFragment StoreEntitySet="MACHINES_EVENTS"> + <ScalarProperty Name="IS_SYNCHRONIZED" ColumnName="IS_SYNCHRONIZED" /> <ScalarProperty Name="DESCRIPTION" ColumnName="DESCRIPTION" /> <ScalarProperty Name="DATE_TIME" ColumnName="DATE_TIME" /> <ScalarProperty Name="USER_GUID" ColumnName="USER_GUID" /> 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 @@ <!-- Diagram content (shape and connector positions) --> <edmx:Diagrams> <Diagram DiagramId="f9ae01d708754bbd997add25a4bacc79" Name="Diagram1"> - <EntityTypeShape EntityType="RemoteModel.ADDRESS" Width="1.5" PointX="0.75" PointY="56.375" /> - <EntityTypeShape EntityType="RemoteModel.APPLICATION_DISPLAY_PANEL_VERSIONS" Width="1.5" PointX="0.75" PointY="36.5" /> - <EntityTypeShape EntityType="RemoteModel.APPLICATION_FIRMWARE_VERSIONS" Width="1.5" PointX="0.75" PointY="45.25" /> - <EntityTypeShape EntityType="RemoteModel.APPLICATION_OS_VERSIONS" Width="1.5" PointX="0.75" PointY="42.25" /> - <EntityTypeShape EntityType="RemoteModel.BRUSH_STOPS" Width="1.5" PointX="15" PointY="17.25" /> - <EntityTypeShape EntityType="RemoteModel.CARTRIDGE_TYPES" Width="1.5" PointX="11.25" PointY="32.875" /> - <EntityTypeShape EntityType="RemoteModel.CAT" Width="1.5" PointX="7.5" PointY="20" /> - <EntityTypeShape EntityType="RemoteModel.CCT" Width="1.5" PointX="0.75" PointY="29.75" /> - <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS" Width="1.5" PointX="1.5" PointY="7.875" /> - <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS_GROUPS" Width="1.5" PointX="3.75" PointY="8.25" /> - <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS_ITEMS" Width="1.5" PointX="6" PointY="7" /> - <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS_ITEMS_RECIPES" Width="1.5" PointX="8.25" PointY="15.875" /> - <EntityTypeShape EntityType="RemoteModel.COLOR_SPACES" Width="1.5" PointX="3" PointY="3.5" /> - <EntityTypeShape EntityType="RemoteModel.CONFIGURATION" Width="1.5" PointX="3" PointY="42" /> - <EntityTypeShape EntityType="RemoteModel.CONTACT" Width="1.5" PointX="0.75" PointY="52.5" /> - <EntityTypeShape EntityType="RemoteModel.CUSTOMER" Width="1.5" PointX="8.25" PointY="28.5" /> - <EntityTypeShape EntityType="RemoteModel.DISPENSER_TYPES" Width="1.5" PointX="9" PointY="58.625" /> - <EntityTypeShape EntityType="RemoteModel.DISPENSER" Width="1.5" PointX="11.25" PointY="58.125" /> - <EntityTypeShape EntityType="RemoteModel.EMBEDDED_FIRMWARE_VERSIONS" Width="1.5" PointX="0.75" PointY="39.375" /> - <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES" Width="1.5" PointX="11.25" PointY="27.5" /> - <EntityTypeShape EntityType="RemoteModel.FIBER_SHAPES" Width="1.5" PointX="0.75" PointY="15.875" /> - <EntityTypeShape EntityType="RemoteModel.FIBER_SYNTHS" Width="1.5" PointX="0.75" PointY="20.5" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_BLOWER_TYPES" Width="1.5" PointX="0.75" PointY="60.75" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_BLOWERS" Width="1.5" PointX="3" PointY="48.375" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_BREAK_SENSOR_TYPES" Width="1.5" PointX="3.75" PointY="38.75" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_BREAK_SENSORS" Width="1.5" PointX="6" PointY="42.5" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_DANCER_TYPES" Width="1.5" PointX="9.75" PointY="62.75" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_DANCERS" Width="1.5" PointX="12" PointY="44.625" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_MOTOR_TYPES" Width="1.5" PointX="6.75" PointY="38.625" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_MOTORS" Width="1.5" PointX="9" PointY="40.5" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_PID_CONTROL_TYPES" Width="1.5" PointX="7.75" PointY="66.75" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_PID_CONTROLS" Width="1.5" PointX="10" PointY="50.75" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_SPEED_SENSOR_TYPES" Width="1.5" PointX="3.75" PointY="34.75" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_SPEED_SENSORS" Width="1.5" PointX="6" PointY="46.5" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_VERSIONS" Width="1.5" PointX="0.75" PointY="48.125" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_WINDER_TYPES" Width="1.5" PointX="3.75" PointY="58.75" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_WINDERS" Width="1.5" PointX="6" PointY="57.5" /> - <EntityTypeShape EntityType="RemoteModel.IDS_PACK_FORMULAS" Width="1.5" PointX="11.25" PointY="35.75" /> - <EntityTypeShape EntityType="RemoteModel.IDS_PACKS" Width="1.5" PointX="13.5" PointY="31.125" /> - <EntityTypeShape EntityType="RemoteModel.JOB_RUNS" Width="1.5" PointX="10.75" PointY="0.75" /> + <EntityTypeShape EntityType="RemoteModel.ADDRESS" Width="1.5" PointX="3.75" PointY="58.25" /> + <EntityTypeShape EntityType="RemoteModel.APPLICATION_DISPLAY_PANEL_VERSIONS" Width="1.5" PointX="0.75" PointY="36.75" /> + <EntityTypeShape EntityType="RemoteModel.APPLICATION_FIRMWARE_VERSIONS" Width="1.5" PointX="0.75" PointY="49.75" /> + <EntityTypeShape EntityType="RemoteModel.APPLICATION_OS_VERSIONS" Width="1.5" PointX="0.75" PointY="46.875" /> + <EntityTypeShape EntityType="RemoteModel.BRUSH_STOPS" Width="1.5" PointX="15" PointY="17.375" /> + <EntityTypeShape EntityType="RemoteModel.CARTRIDGE_TYPES" Width="1.5" PointX="9" PointY="12.25" /> + <EntityTypeShape EntityType="RemoteModel.CAT" Width="1.5" PointX="7.5" PointY="24.125" /> + <EntityTypeShape EntityType="RemoteModel.CCT" Width="1.5" PointX="0.75" PointY="24" /> + <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS" Width="1.5" PointX="1.5" PointY="8" /> + <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS_GROUPS" Width="1.5" PointX="3.75" PointY="8.375" /> + <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS_ITEMS" Width="1.5" PointX="6" PointY="7.125" /> + <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS_ITEMS_RECIPES" Width="1.5" PointX="8.25" PointY="16" /> + <EntityTypeShape EntityType="RemoteModel.COLOR_SPACES" Width="1.5" PointX="8.25" PointY="43.25" /> + <EntityTypeShape EntityType="RemoteModel.CONFIGURATION" Width="1.5" PointX="3" PointY="42.25" /> + <EntityTypeShape EntityType="RemoteModel.CONTACT" Width="1.5" PointX="3.75" PointY="62.375" /> + <EntityTypeShape EntityType="RemoteModel.CUSTOMER" Width="1.5" PointX="8.25" PointY="60.75" /> + <EntityTypeShape EntityType="RemoteModel.DISPENSER_TYPES" Width="1.5" PointX="6.75" PointY="3.125" /> + <EntityTypeShape EntityType="RemoteModel.DISPENSER" Width="1.5" PointX="9" PointY="2.5" /> + <EntityTypeShape EntityType="RemoteModel.EMBEDDED_FIRMWARE_VERSIONS" Width="1.5" PointX="0.75" PointY="39.625" /> + <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES" Width="1.5" PointX="11.25" PointY="11.625" /> + <EntityTypeShape EntityType="RemoteModel.FIBER_SHAPES" Width="1.5" PointX="0.75" PointY="30.125" /> + <EntityTypeShape EntityType="RemoteModel.FIBER_SYNTHS" Width="1.5" PointX="0.75" PointY="20.625" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_BLOWER_TYPES" Width="1.5" PointX="3.75" PointY="34.125" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_BLOWERS" Width="1.5" PointX="6" PointY="37.75" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_BREAK_SENSOR_TYPES" Width="1.5" PointX="6.75" PointY="65.125" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_BREAK_SENSORS" Width="1.5" PointX="9" PointY="38.875" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_DANCER_TYPES" Width="1.5" PointX="3.75" PointY="67.125" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_DANCERS" Width="1.5" PointX="6" PointY="42.125" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_MOTOR_TYPES" Width="1.5" PointX="8.75" PointY="69" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_MOTORS" Width="1.5" PointX="11" PointY="42.875" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_PID_CONTROL_TYPES" Width="1.5" PointX="0.75" PointY="57.125" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_PID_CONTROLS" Width="1.5" PointX="3" PointY="47.25" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_SPEED_SENSOR_TYPES" Width="1.5" PointX="0.75" PointY="53.125" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_SPEED_SENSORS" Width="1.5" PointX="3" PointY="37.875" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_VERSIONS" Width="1.5" PointX="0.75" PointY="42.5" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_WINDER_TYPES" Width="1.5" PointX="8.75" PointY="47.125" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_WINDERS" Width="1.5" PointX="11" PointY="34.875" /> + <EntityTypeShape EntityType="RemoteModel.IDS_PACK_FORMULAS" Width="1.5" PointX="9" PointY="8.125" /> + <EntityTypeShape EntityType="RemoteModel.IDS_PACKS" Width="1.5" PointX="11.25" PointY="27.5" /> + <EntityTypeShape EntityType="RemoteModel.JOB_RUNS" Width="1.5" PointX="0.75" PointY="0.75" /> <EntityTypeShape EntityType="RemoteModel.JOB" Width="1.5" PointX="10.5" PointY="16.75" /> - <EntityTypeShape EntityType="RemoteModel.LINEAR_MASS_DENSITY_UNITS" Width="1.5" PointX="0.75" PointY="26.75" /> - <EntityTypeShape EntityType="RemoteModel.LIQUID_TYPES" Width="1.5" PointX="5.25" PointY="2.375" /> - <EntityTypeShape EntityType="RemoteModel.LIQUID_TYPES_RMLS" Width="1.5" PointX="8.25" PointY="24.25" /> - <EntityTypeShape EntityType="RemoteModel.MACHINE_STUDIO_VERSIONS" Width="1.5" PointX="10.5" PointY="13.25" /> - <EntityTypeShape EntityType="RemoteModel.MACHINE_VERSIONS" Width="1.5" PointX="3" PointY="12.125" /> - <EntityTypeShape EntityType="RemoteModel.MACHINE" Width="1.5" PointX="5.25" PointY="17" /> - <EntityTypeShape EntityType="RemoteModel.MACHINES_EVENTS" Width="1.5" PointX="13.5" PointY="26.875" /> - <EntityTypeShape EntityType="RemoteModel.MEDIA_CONDITIONS" Width="1.5" PointX="0.75" PointY="33" /> - <EntityTypeShape EntityType="RemoteModel.MEDIA_MATERIALS" Width="1.5" PointX="0.75" PointY="23.875" /> - <EntityTypeShape EntityType="RemoteModel.MEDIA_PURPOSES" Width="1.5" PointX="0.75" PointY="13" /> - <EntityTypeShape EntityType="RemoteModel.MID_TANK_TYPES" Width="1.5" PointX="11.25" PointY="39.375" /> - <EntityTypeShape EntityType="RemoteModel.ORGANIZATION" Width="1.5" PointX="3" PointY="54.5" /> - <EntityTypeShape EntityType="RemoteModel.PERMISSION" Width="1.5" PointX="8.25" PointY="2.25" /> - <EntityTypeShape EntityType="RemoteModel.PROCESS_PARAMETERS_TABLES" Width="1.5" PointX="7.5" PointY="50.25" /> - <EntityTypeShape EntityType="RemoteModel.PROCESS_PARAMETERS_TABLES_GROUPS" Width="1.5" PointX="5.25" PointY="52" /> - <EntityTypeShape EntityType="RemoteModel.RML" Width="1.5" PointX="3" PointY="17" /> - <EntityTypeShape EntityType="RemoteModel.ROLE" Width="1.5" PointX="8.25" PointY="6.25" /> - <EntityTypeShape EntityType="RemoteModel.ROLES_PERMISSIONS" Width="1.5" PointX="10.5" PointY="5.25" /> - <EntityTypeShape EntityType="RemoteModel.SEGMENT" Width="1.5" PointX="12.75" PointY="20.25" /> - <EntityTypeShape EntityType="RemoteModel.SPOOL_TYPES" Width="1.5" PointX="3" PointY="27.375" /> - <EntityTypeShape EntityType="RemoteModel.SPOOL" Width="1.5" PointX="13.5" PointY="13" /> - <EntityTypeShape EntityType="RemoteModel.sysdiagram" Width="1.5" PointX="12.75" PointY="0.75" /> - <EntityTypeShape EntityType="RemoteModel.TANGO_UPDATES" Width="1.5" PointX="12.75" PointY="3.75" /> - <EntityTypeShape EntityType="RemoteModel.TANGO_VERSIONS" Width="1.5" PointX="13.5" PointY="8.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_CONTROLLERS" Width="1.5" PointX="14.75" PointY="0.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_DISPENSERS" Width="1.5" PointX="14.75" PointY="4.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_HEATERS" Width="1.5" PointX="15.75" PointY="7.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_IOS" Width="1.5" PointX="15.75" PointY="10.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_MONITORS" Width="1.5" PointX="16.75" PointY="0.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_VALVES" Width="1.5" PointX="17.75" PointY="5.75" /> - <EntityTypeShape EntityType="RemoteModel.USER" Width="1.5" PointX="8.25" PointY="10.25" /> - <EntityTypeShape EntityType="RemoteModel.USERS_ROLES" Width="1.5" PointX="10.5" PointY="9.25" /> - <EntityTypeShape EntityType="RemoteModel.WINDING_METHODS" Width="1.5" PointX="8.25" PointY="31.625" /> + <EntityTypeShape EntityType="RemoteModel.LINEAR_MASS_DENSITY_UNITS" Width="1.5" PointX="0.75" PointY="13.125" /> + <EntityTypeShape EntityType="RemoteModel.LIQUID_TYPES" Width="1.5" PointX="6" PointY="13" /> + <EntityTypeShape EntityType="RemoteModel.LIQUID_TYPES_RMLS" Width="1.5" PointX="8.25" PointY="20.375" /> + <EntityTypeShape EntityType="RemoteModel.MACHINE_STUDIO_VERSIONS" Width="1.5" PointX="10.5" PointY="52.875" /> + <EntityTypeShape EntityType="RemoteModel.MACHINE_VERSIONS" Width="1.5" PointX="3" PointY="12.375" /> + <EntityTypeShape EntityType="RemoteModel.MACHINE" Width="1.5" PointX="5.25" PointY="17.125" /> + <EntityTypeShape EntityType="RemoteModel.MACHINES_EVENTS" Width="1.5" PointX="13.5" PointY="12.875" /> + <EntityTypeShape EntityType="RemoteModel.MEDIA_CONDITIONS" Width="1.5" PointX="0.75" PointY="33.125" /> + <EntityTypeShape EntityType="RemoteModel.MEDIA_MATERIALS" Width="1.5" PointX="0.75" PointY="27.25" /> + <EntityTypeShape EntityType="RemoteModel.MEDIA_PURPOSES" Width="1.5" PointX="0.75" PointY="16" /> + <EntityTypeShape EntityType="RemoteModel.MID_TANK_TYPES" Width="1.5" PointX="9" PointY="64.75" /> + <EntityTypeShape EntityType="RemoteModel.ORGANIZATION" Width="1.5" PointX="6" PointY="60.25" /> + <EntityTypeShape EntityType="RemoteModel.PERMISSION" Width="1.5" PointX="11.25" PointY="57" /> + <EntityTypeShape EntityType="RemoteModel.PROCESS_PARAMETERS_TABLES" Width="1.5" PointX="7.5" PointY="50.625" /> + <EntityTypeShape EntityType="RemoteModel.PROCESS_PARAMETERS_TABLES_GROUPS" Width="1.5" PointX="5.25" PointY="52.25" /> + <EntityTypeShape EntityType="RemoteModel.RML" Width="1.5" PointX="3" PointY="17.125" /> + <EntityTypeShape EntityType="RemoteModel.ROLE" Width="1.5" PointX="11.25" PointY="38.875" /> + <EntityTypeShape EntityType="RemoteModel.ROLES_PERMISSIONS" Width="1.5" PointX="13.5" PointY="39" /> + <EntityTypeShape EntityType="RemoteModel.SEGMENT" Width="1.5" PointX="12.75" PointY="20.375" /> + <EntityTypeShape EntityType="RemoteModel.SPOOL_TYPES" Width="1.5" PointX="3" PointY="29.625" /> + <EntityTypeShape EntityType="RemoteModel.SPOOL" Width="1.5" PointX="13.5" PointY="27.125" /> + <EntityTypeShape EntityType="RemoteModel.sysdiagram" Width="1.5" PointX="2.75" PointY="0.75" /> + <EntityTypeShape EntityType="RemoteModel.TANGO_UPDATES" Width="1.5" PointX="2.75" PointY="3.75" /> + <EntityTypeShape EntityType="RemoteModel.TANGO_VERSIONS" Width="1.5" PointX="16.5" PointY="27.25" /> + <EntityTypeShape EntityType="RemoteModel.TECH_CONTROLLERS" Width="1.5" PointX="11.75" PointY="0.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_DISPENSERS" Width="1.5" PointX="11.75" PointY="4.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_HEATERS" Width="1.5" PointX="11.75" PointY="7.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_IOS" Width="1.5" PointX="13.75" PointY="0.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_MONITORS" Width="1.5" PointX="13.75" PointY="5.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_VALVES" Width="1.5" PointX="15.75" PointY="0.75" /> + <EntityTypeShape EntityType="RemoteModel.USER" Width="1.5" PointX="8.25" PointY="28.875" /> + <EntityTypeShape EntityType="RemoteModel.USERS_ROLES" Width="1.5" PointX="13.5" PointY="31" /> + <EntityTypeShape EntityType="RemoteModel.WINDING_METHODS" Width="1.5" PointX="8.25" PointY="34.125" /> <AssociationConnector Association="RemoteModel.FK_ORGANIZATIONS_ADDRESSES" /> <AssociationConnector Association="RemoteModel.FK_USERS_ADDRESSES" /> <AssociationConnector Association="RemoteModel.FK_CONFIGURATIONS_APPLICATION_DISPLAY_PANEL_VERSIONS" /> 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<TLoginRequest, TLoginResponse> 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<T>() where T : TangoWebClient<TLoginRequest, TLoginResponse> + { + return Activator.CreateInstance(typeof(T), new object[] { this }) as T; + } + public async Task<TLoginResponse> Login(TLoginRequest request) { var response = await _client.PostJson<TLoginRequest, TLoginResponse>(GetActionAddress("Login"), request); @@ -85,7 +109,7 @@ namespace Tango.Web var response = await _client.PostJson<TRequest, TResponse>(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 @@ <add key="MACHINE_STUDIO_VERSIONS_CONTAINER" value="machine-studio-versions-dev" /> <add key="DEPLOYMENT_SLOT" value="DEV" /> <add key="ENVIRONMENT_GROUP" value="Tango DEV" /> - <add key="ENFORCE_MACHINE_STUDIO_VERSION" value="true" /> + <add key="ENFORCE_MACHINE_STUDIO_VERSION" value="false" /> <add key="JWT_TOKEN_SECRET" value="GQDstcKsx0NHjLOuXOYg5MbeJ1yT0u1iwDVTwine" /> <add key="REFRESH_TOKENS_TABLE_NAME" value="RefreshTokens" /> <add key="REFRESH_TOKENS_TABLE_PARTITION" value="REFRESH_TOKENS_PART" /> @@ -62,6 +62,11 @@ </location> <system.webServer> + <security> + <requestFiltering> + <requestLimits maxAllowedContentLength="2147483648" /> + </requestFiltering> + </security> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="OPTIONSVerbHandler" /> |
