aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels')
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/DirectSynchronizationViewVM.cs328
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/LocalSynchronizationViewVM.cs363
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/MainViewVM.cs61
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/MenuViewVM.cs31
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/RemoteSynchronizationViewVM.cs372
5 files changed, 1155 insertions, 0 deletions
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/DirectSynchronizationViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/DirectSynchronizationViewVM.cs
new file mode 100644
index 000000000..3f8772f0e
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/DirectSynchronizationViewVM.cs
@@ -0,0 +1,328 @@
+using Google.Protobuf;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Data.Entity.Validation;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Tango.Core.Commands;
+using Tango.Core.Helpers;
+using Tango.DAL.Observables;
+using Tango.Integration.Services;
+using Tango.MachineStudio.Common.Notifications;
+using Tango.MachineStudio.Common.StudioApplication;
+using Tango.MachineStudio.Synchronization.Navigation;
+using Tango.PMR.Integration;
+using Tango.SharedUI;
+using Tango.Synchronization;
+using Tango.Synchronization.Local;
+using Tango.Synchronization.Remote;
+
+namespace Tango.MachineStudio.Synchronization.ViewModels
+{
+ public class DirectSynchronizationViewVM : ViewModel
+ {
+ private SyncNavigationManager _navigation;
+ private String _slaveDBFile;
+ private String _masterDBFile;
+ private LocalDBComparer _comparer;
+ private INotificationProvider _notification;
+ private String _comparedSerialNumber;
+
+ public IStudioApplicationManager ApplicationManager { get; set; }
+
+ public DirectSynchronizationViewVM(IStudioApplicationManager applicationManager, SyncNavigationManager navigation, INotificationProvider notification)
+ {
+ ApplicationManager = applicationManager;
+
+ _navigation = navigation;
+ _notification = notification;
+
+ BackCommand = new RelayCommand(() => _navigation.NavigateTo(NavigationView.MenuView));
+
+ Differences = new ObservableCollection<Diff>();
+
+ CompareCommand = new RelayCommand(Compare, (x) => !IsWorking && SelectedMachine != null);
+ CommitAllCommand = new RelayCommand(Synchronize, (x) => Differences.Count > 0 && !IsWorking && SelectedMachine != null);
+ }
+
+ #region Commands
+
+ /// <summary>
+ /// Gets or sets the back command.
+ /// </summary>
+ public RelayCommand BackCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the browse master database command.
+ /// </summary>
+ public RelayCommand BrowseMasterDBCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the browse slave database command.
+ /// </summary>
+ public RelayCommand BrowseSlaveDBCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the compare command.
+ /// </summary>
+ public RelayCommand CompareCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the commit command.
+ /// </summary>
+ public RelayCommand CommitCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the commit all command.
+ /// </summary>
+ public RelayCommand CommitAllCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the clean command.
+ /// </summary>
+ public RelayCommand CleanCommand { get; set; }
+
+ #endregion
+
+ #region Properties
+
+ private bool _isWorking;
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is working.
+ /// </summary>
+ public bool IsWorking
+ {
+ get { return _isWorking; }
+ set { _isWorking = value; RaisePropertyChangedAuto(); }
+ }
+
+ private bool _isClearMachine;
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is clear machine.
+ /// </summary>
+ public bool IsClearMachine
+ {
+ get { return _isClearMachine; }
+ set
+ {
+ _isClearMachine = value;
+ RaisePropertyChangedAuto();
+ }
+ }
+
+ private ObservableCollection<Diff> _differences;
+ /// <summary>
+ /// Gets or sets the differences.
+ /// </summary>
+ public ObservableCollection<Diff> Differences
+ {
+ get { return _differences; }
+ set { _differences = value; RaisePropertyChanged(nameof(Differences)); }
+ }
+
+ private Diff _selectedDifference;
+ /// <summary>
+ /// Gets or sets the selected difference.
+ /// </summary>
+ public Diff SelectedDifference
+ {
+ get { return _selectedDifference; }
+ set { _selectedDifference = value; RaisePropertyChanged(nameof(SelectedDifference)); InvalidateRelayCommands(); }
+ }
+
+ private Machine _selectedMachine;
+ /// <summary>
+ /// Gets or sets the selected machine.
+ /// </summary>
+ public Machine SelectedMachine
+ {
+ get { return _selectedMachine; }
+ set { _selectedMachine = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private void Compare()
+ {
+ if (SelectedMachine.SerialNumber != ApplicationManager.ConnectedMachine.SerialNumber)
+ {
+ if (!_notification.ShowQuestion("The selected machine serial number does not match the connected machine. Are you sure you want to continue?"))
+ {
+ return;
+ }
+ }
+
+ Task.Factory.StartNew(async () =>
+ {
+ using (_notification.PushTaskItem("Downloading machine database..."))
+ {
+ try
+ {
+ IsWorking = true;
+ InvalidateRelayCommands();
+ Thread.Sleep(1500);
+
+ ApplicationManager.ConnectedMachine.UseKeepAlive = false;
+
+ var response = await ApplicationManager.ConnectedMachine.SendRequest<DirectSynchronizationRequest, DirectSynchronizationResponse>(new DirectSynchronizationRequest(), TimeSpan.FromSeconds(60));
+
+ using (_notification.PushTaskItem("Generating temporary files..."))
+ {
+ String tempFolder = PathHelper.GetTempFolderPath();
+
+ //File path for the reflected remote data base SQLite.
+ _masterDBFile = Path.Combine(tempFolder, "Remote.db");
+ //File path for the received machine SQLite db.
+ _slaveDBFile = Path.Combine(tempFolder, "Local.db");
+
+ //Save the machine db to file.
+ File.WriteAllBytes(_slaveDBFile, response.Message.LocalDB.ToByteArray());
+
+ //Copy the SQLite db template.
+ File.Copy(Path.Combine(PathHelper.GetStartupPath(), "Tango.db"), _masterDBFile);
+
+ //Synchronize the SQL Server db with the new SQLite template. (Overwrite basically)
+ RemoteDBSynchronizer.Synchronize(_masterDBFile, SelectedMachine.SerialNumber, true);
+
+ _comparedSerialNumber = SelectedMachine.SerialNumber;
+
+ using (_notification.PushTaskItem("Comparing database..."))
+ {
+ _comparer = new LocalDBComparer(new SQLiteDataBase(_masterDBFile), new SQLiteDataBase(_slaveDBFile));
+
+ var diffs = _comparer.Compare();
+ Differences = new ObservableCollection<Diff>(diffs);
+
+ if (diffs.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() > 0)
+ {
+ ShowInfo("Found " + Differences.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() + " differences.");
+ }
+ else
+ {
+ ShowInfo("The machine database is synchronized.");
+ }
+ }
+ }
+ }
+ catch (DbEntityValidationException ex)
+ {
+ String message = "The following validation errors occurred." + Environment.NewLine + Environment.NewLine;
+
+ foreach (var error in ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).ToList())
+ {
+ message += error.ErrorMessage + Environment.NewLine;
+ }
+
+ ShowError(message);
+ }
+ catch (Exception ex)
+ {
+ ShowError(ex.Message);
+ }
+ finally
+ {
+ IsWorking = false;
+ SelectedDifference = null;
+ InvalidateRelayCommands();
+ ApplicationManager.ConnectedMachine.UseKeepAlive = true;
+ }
+ }
+ });
+ }
+
+ private void Synchronize()
+ {
+ if (IsClearMachine)
+ {
+ if (!_notification.ShowQuestion("This will erase and override the existing machine database. Do you want to proceed?"))
+ {
+ return;
+ }
+ }
+
+ if (SelectedMachine.SerialNumber != _comparedSerialNumber)
+ {
+ _notification.ShowError("You have selected a different machine serial number after comparing. Please compare again if you wish to synchronize a different machine.");
+ return;
+ }
+
+ Task.Factory.StartNew(async () =>
+ {
+ using (_notification.PushTaskItem("Synchronizing..."))
+ {
+ try
+ {
+ ApplicationManager.ConnectedMachine.UseKeepAlive = false;
+
+ IsWorking = true;
+ InvalidateRelayCommands();
+ Thread.Sleep(1500);
+
+ for (int i = 0; i < Differences.Count; i++)
+ {
+ var diff = Differences[i];
+
+ diff.Commit();
+ InvokeUINow(() => Differences.Remove(diff));
+ i--;
+ }
+
+ _comparer.Dispose();
+
+ byte[] remoteDbBytes = IsClearMachine ? File.ReadAllBytes(_masterDBFile) : File.ReadAllBytes(_slaveDBFile);
+
+ var response = await ApplicationManager.ConnectedMachine.SendRequest<OverrideDataBaseRequest, OverrideDataBaseResponse>(new OverrideDataBaseRequest()
+ {
+ RemoteDB = ByteString.CopyFrom(remoteDbBytes)
+ }, TimeSpan.FromSeconds(30));
+
+ if (!response.Message.Successful)
+ {
+ ShowError("The remote machine has reported some error while trying to override the database.");
+ }
+ else
+ {
+ //Synchronize the SQL Server db with the synchronized master DB.
+ RemoteDBSynchronizer.Synchronize(_masterDBFile, _comparedSerialNumber, false);
+
+ PathHelper.TryDeleteFile(_slaveDBFile);
+ PathHelper.TryDeleteFile(_masterDBFile);
+ _slaveDBFile = null;
+ _masterDBFile = null;
+ InvalidateRelayCommands();
+ }
+ }
+ catch (Exception ex)
+ {
+ ShowError(ex.Message);
+ }
+ finally
+ {
+ IsWorking = false;
+ SelectedDifference = null;
+ InvalidateRelayCommands();
+ ApplicationManager.ConnectedMachine.UseKeepAlive = true;
+ }
+ }
+ });
+ }
+
+ private void ShowError(String message)
+ {
+ InvokeUINow(() => _notification.ShowError(message));
+ }
+
+ private void ShowInfo(String message)
+ {
+ InvokeUINow(() => _notification.ShowInfo(message));
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/LocalSynchronizationViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/LocalSynchronizationViewVM.cs
new file mode 100644
index 000000000..9c805cca4
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/LocalSynchronizationViewVM.cs
@@ -0,0 +1,363 @@
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Tango.Core.Commands;
+using Tango.Logging;
+using Tango.MachineStudio.Common.Notifications;
+using Tango.MachineStudio.Synchronization.Navigation;
+using Tango.MachineStudio.Synchronization.Properties;
+using Tango.Settings;
+using Tango.SharedUI;
+using Tango.Synchronization;
+using Tango.Synchronization.Local;
+
+namespace Tango.MachineStudio.Synchronization.ViewModels
+{
+ public class LocalSynchronizationViewVM : ViewModel
+ {
+ private SyncNavigationManager _navigation;
+ private String _masterDBFile;
+ private String _slaveDBFile;
+ private LocalDBComparer _comparer;
+ private INotificationProvider _notification;
+ private bool _isWorking;
+
+ #region Constructors
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LocalSynchronizationViewVM"/> class.
+ /// </summary>
+ /// <param name="navigation">The navigation.</param>
+ /// <param name="notification">The notification.</param>
+ public LocalSynchronizationViewVM(SyncNavigationManager navigation, INotificationProvider notification)
+ {
+ _navigation = navigation;
+ _notification = notification;
+
+ BackCommand = new RelayCommand(() => _navigation.NavigateTo(NavigationView.MenuView));
+
+ Differences = new ObservableCollection<Diff>();
+
+ BrowseMasterDBCommand = new RelayCommand(BrowseMasterDB, (x) => !_isWorking);
+ BrowseSlaveDBCommand = new RelayCommand(BrowseSlaveDB, (x) => !_isWorking);
+ CompareCommand = new RelayCommand(Compare, (x) => MasterDBFile != null && SlaveDBFile != null && !_isWorking);
+ CommitCommand = new RelayCommand(Commit, (x) => SelectedDifference != null && !_isWorking);
+ CommitAllCommand = new RelayCommand(CommitAll, (x) => Differences.Count > 0 && !_isWorking);
+ CleanCommand = new RelayCommand(CleanSlave, (x) => !_isWorking && SlaveDBFile != null);
+
+ if (File.Exists(SettingsManager.Default.MachineStudio.SynchronizationModule.LocalMasterDBFile))
+ {
+ MasterDBFile = SettingsManager.Default.MachineStudio.SynchronizationModule.LocalMasterDBFile;
+ MasterDBName = Path.GetFileName(MasterDBFile);
+ }
+
+ if (File.Exists(SettingsManager.Default.MachineStudio.SynchronizationModule.LocalSlaveDBFile))
+ {
+ SlaveDBFile = SettingsManager.Default.MachineStudio.SynchronizationModule.LocalSlaveDBFile;
+ SlaveDBName = Path.GetFileName(SlaveDBFile);
+ }
+ }
+
+ #endregion
+
+ #region Commands
+
+ /// <summary>
+ /// Gets or sets the back command.
+ /// </summary>
+ public RelayCommand BackCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the browse master database command.
+ /// </summary>
+ public RelayCommand BrowseMasterDBCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the browse slave database command.
+ /// </summary>
+ public RelayCommand BrowseSlaveDBCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the compare command.
+ /// </summary>
+ public RelayCommand CompareCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the commit command.
+ /// </summary>
+ public RelayCommand CommitCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the commit all command.
+ /// </summary>
+ public RelayCommand CommitAllCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the clean command.
+ /// </summary>
+ public RelayCommand CleanCommand { get; set; }
+ #endregion
+
+ #region Properties
+
+ private ObservableCollection<Diff> _differences;
+ /// <summary>
+ /// Gets or sets the differences.
+ /// </summary>
+ public ObservableCollection<Diff> Differences
+ {
+ get { return _differences; }
+ set { _differences = value; RaisePropertyChanged(nameof(Differences)); }
+ }
+
+ private Diff _selectedDifference;
+ /// <summary>
+ /// Gets or sets the selected difference.
+ /// </summary>
+ public Diff SelectedDifference
+ {
+ get { return _selectedDifference; }
+ set { _selectedDifference = value; RaisePropertyChanged(nameof(SelectedDifference)); InvalidateRelayCommands(); }
+ }
+
+ private String _masterDBName;
+ /// <summary>
+ /// Gets or sets the name of the master database.
+ /// </summary>
+ public String MasterDBName
+ {
+ get { return _masterDBName; }
+ set { _masterDBName = value; RaisePropertyChanged(nameof(MasterDBName)); }
+ }
+
+ private String _slaveDBName;
+ /// <summary>
+ /// Gets or sets the name of the slave database.
+ /// </summary>
+ public String SlaveDBName
+ {
+ get { return _slaveDBName; }
+ set { _slaveDBName = value; RaisePropertyChanged(nameof(SlaveDBName)); }
+ }
+
+ /// <summary>
+ /// Gets or sets the slave database file.
+ /// </summary>
+ public String SlaveDBFile
+ {
+ get { return _slaveDBFile; }
+ set { _slaveDBFile = value; RaisePropertyChangedAuto(); }
+ }
+
+ /// <summary>
+ /// Gets or sets the master database file.
+ /// </summary>
+ public String MasterDBFile
+ {
+ get { return _masterDBFile; }
+ set { _masterDBFile = value; RaisePropertyChangedAuto(); }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private async void CleanSlave()
+ {
+ if (_notification.ShowQuestion("Are you sure you want to erase all data on slave database?"))
+ {
+ using (_notification.PushTaskItem("Clearing database..."))
+ {
+ try
+ {
+ _isWorking = true;
+ await Task.Factory.StartNew(() =>
+ {
+ SQLiteDataBase localDB = new SQLiteDataBase(SlaveDBFile);
+ localDB.LoadTables();
+ localDB.ClearDataBase();
+ try
+ {
+ localDB.Dispose();
+ }
+ catch { }
+ });
+
+ Differences.Clear();
+ }
+ catch (Exception ex)
+ {
+ ShowError(LogManager.Log(ex).Message);
+ }
+ finally
+ {
+ _isWorking = false;
+ InvalidateRelayCommands();
+ SelectedDifference = null;
+ }
+ }
+ }
+ }
+
+ private void Compare()
+ {
+ _comparer = new LocalDBComparer(new SQLiteDataBase(MasterDBFile), new SQLiteDataBase(SlaveDBFile));
+
+ Task.Factory.StartNew(() =>
+ {
+ using (_notification.PushTaskItem("Comparing Databases..."))
+ {
+ try
+ {
+ _isWorking = true;
+ InvalidateRelayCommands();
+ Thread.Sleep(1500);
+ var diffs = _comparer.Compare();
+ Differences = new ObservableCollection<Diff>(diffs);
+
+ if (diffs.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() > 0)
+ {
+ ShowInfo("Found " + Differences.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() + " differences.");
+ }
+ else
+ {
+ ShowInfo("The master and slave databases are synchronized.");
+ }
+ }
+ catch (Exception ex)
+ {
+ ShowError(ex.Message);
+ }
+ finally
+ {
+ _isWorking = false;
+ SelectedDifference = null;
+ InvalidateRelayCommands();
+
+ SettingsManager.Default.MachineStudio.SynchronizationModule.LocalMasterDBFile = MasterDBFile;
+ SettingsManager.Default.MachineStudio.SynchronizationModule.LocalSlaveDBFile = SlaveDBFile;
+ SettingsManager.SaveDefaultSettings();
+ }
+ }
+ });
+ }
+
+ private void Commit()
+ {
+ Task.Factory.StartNew(() =>
+ {
+ using (_notification.PushTaskItem("Committing difference..."))
+ {
+ try
+ {
+ _isWorking = true;
+ InvalidateRelayCommands();
+ Thread.Sleep(1500);
+ SelectedDifference.Commit();
+
+ InvokeUINow(() => Differences.Remove(SelectedDifference));
+ }
+ catch (Exception ex)
+ {
+ ShowError(ex.Message);
+ }
+ finally
+ {
+ _isWorking = false;
+ SelectedDifference = null;
+ InvalidateRelayCommands();
+ }
+ }
+ });
+ }
+
+ private void CommitAll()
+ {
+ Task.Factory.StartNew(() =>
+ {
+ using (_notification.PushTaskItem("Committing all differences..."))
+ {
+ try
+ {
+ _isWorking = true;
+ InvalidateRelayCommands();
+ Thread.Sleep(1500);
+
+ for (int i = 0; i < Differences.Count; i++)
+ {
+ var diff = Differences[i];
+ using (_notification.PushTaskItem("Committing difference " + (Differences.IndexOf(diff) + 1) + "..."))
+ {
+ diff.Commit();
+ InvokeUINow(() => Differences.Remove(diff));
+ i--;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ ShowError(ex.Message);
+ }
+ finally
+ {
+ _isWorking = false;
+ SelectedDifference = null;
+ InvalidateRelayCommands();
+ }
+ }
+ });
+ }
+
+ private void BrowseSlaveDB()
+ {
+ String file = BrowseForFilePath();
+ if (file != null)
+ {
+ SlaveDBFile = file;
+ SlaveDBName = Path.GetFileName(file);
+ InvalidateRelayCommands();
+ }
+ }
+
+ private void BrowseMasterDB()
+ {
+ String file = BrowseForFilePath();
+ if (file != null)
+ {
+ MasterDBFile = file;
+ MasterDBName = Path.GetFileName(file);
+ InvalidateRelayCommands();
+ }
+ }
+
+ private String BrowseForFilePath()
+ {
+ OpenFileDialog dlg = new OpenFileDialog();
+ dlg.Title = "Select SQLite Database File";
+ dlg.Filter = "SQLite Database|*.db";
+ if (dlg.ShowDialog().Value)
+ {
+ return dlg.FileName;
+ }
+ return null;
+ }
+
+ private void ShowError(String message)
+ {
+ InvokeUINow(() => _notification.ShowError(message));
+ }
+
+ private void ShowInfo(String message)
+ {
+ InvokeUINow(() => _notification.ShowInfo(message));
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/MainViewVM.cs
new file mode 100644
index 000000000..c392aee1a
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/MainViewVM.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Logging;
+using Tango.SharedUI;
+
+namespace Tango.MachineStudio.Synchronization.ViewModels
+{
+ public class MainViewVM : ViewModel
+ {
+ public MainViewVM()
+ {
+ MainViewLogger logger = new MainViewLogger();
+ logger.NewLog += (output) =>
+ {
+ Log += output + Environment.NewLine;
+ };
+
+ LogManager.RegisterLogger(logger);
+ }
+
+ private String _log;
+ /// <summary>
+ /// Gets or sets the current application log text.
+ /// </summary>
+ public String Log
+ {
+ get { return _log; }
+ set { _log = value; RaisePropertyChanged(nameof(Log)); }
+ }
+
+ #region Custom Logger
+
+ public class MainViewLogger : ILogger
+ {
+ public bool Enabled { get; set; }
+ public bool Immediate { get; set; }
+ public event Action<String> NewLog;
+
+ public MainViewLogger()
+ {
+ Enabled = true;
+ Immediate = true;
+ }
+
+ public void OnError(LogItemBase output)
+ {
+ NewLog?.Invoke(output.TimeStamp.ToTimeString() + ": " + output.GetMessage());
+ }
+
+ public void OnTrace(LogItemBase output)
+ {
+ NewLog?.Invoke(output.TimeStamp.ToTimeString() + ": " + output.GetMessage());
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/MenuViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/MenuViewVM.cs
new file mode 100644
index 000000000..0fe510f36
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/MenuViewVM.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core.Commands;
+using Tango.MachineStudio.Synchronization.Navigation;
+using Tango.SharedUI;
+
+namespace Tango.MachineStudio.Synchronization.ViewModels
+{
+ public class MenuViewVM : ViewModel
+ {
+ private SyncNavigationManager _navigation;
+
+ public MenuViewVM(SyncNavigationManager navigation)
+ {
+ _navigation = navigation;
+
+ StartLocalSyncCommand = new RelayCommand(() => { _navigation.NavigateTo(NavigationView.LocalSynchronizationView); });
+ StartRemoteSyncCommand = new RelayCommand(() => { _navigation.NavigateTo(NavigationView.RemoteSynchronizationView); });
+ StartDirectRemoteSyncCommand = new RelayCommand(() => { _navigation.NavigateTo(NavigationView.DirectSynchronizationView); });
+ }
+
+ public RelayCommand StartLocalSyncCommand { get; set; }
+
+ public RelayCommand StartRemoteSyncCommand { get; set; }
+
+ public RelayCommand StartDirectRemoteSyncCommand { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/RemoteSynchronizationViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/RemoteSynchronizationViewVM.cs
new file mode 100644
index 000000000..e14b0ffb9
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/RemoteSynchronizationViewVM.cs
@@ -0,0 +1,372 @@
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Data.Entity.Validation;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Tango.Core.Commands;
+using Tango.DAL.Local.DB;
+using Tango.DAL.Observables;
+using Tango.DAL.Remote.DB;
+using Tango.Logging;
+using Tango.MachineStudio.Common.Notifications;
+using Tango.MachineStudio.Common.StudioApplication;
+using Tango.MachineStudio.Synchronization.Navigation;
+using Tango.Settings;
+using Tango.SharedUI;
+using Tango.Synchronization;
+using Tango.Synchronization.Local;
+using Tango.Synchronization.Remote;
+
+namespace Tango.MachineStudio.Synchronization.ViewModels
+{
+ public class RemoteSynchronizationViewVM : ViewModel, IShutdownRequestBlocker
+ {
+ private SyncNavigationManager _navigation;
+ private String _slaveDBFile;
+ private INotificationProvider _notification;
+ private bool _isWorking;
+ private RemoteDBComparer _comparer;
+ private RemoteDB _remoteDB;
+ private LocalDB _localDB;
+
+ #region Constructors
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LocalSynchronizationViewVM"/> class.
+ /// </summary>
+ /// <param name="navigation">The navigation.</param>
+ /// <param name="notification">The notification.</param>
+ public RemoteSynchronizationViewVM(SyncNavigationManager navigation, INotificationProvider notification)
+ {
+ _navigation = navigation;
+ _notification = notification;
+
+ BackCommand = new RelayCommand(() => _navigation.NavigateTo(NavigationView.MenuView));
+
+ Differences = new ObservableCollection<Diff>();
+
+ BrowseSlaveDBCommand = new RelayCommand(BrowseSlaveDB, (x) => !_isWorking);
+ CompareCommand = new RelayCommand(Compare, (x) => SlaveDBFile != null && !_isWorking && SelectedMachine != null);
+ CommitCommand = new RelayCommand(Commit, (x) => SelectedDifference != null && !_isWorking && SelectedMachine != null);
+ CommitAllCommand = new RelayCommand(CommitAll, (x) => Differences.Count > 0 && !_isWorking && SelectedMachine != null);
+ CleanCommand = new RelayCommand(CleanSlave, (x) => !_isWorking && SlaveDBFile != null);
+
+ if (File.Exists(SettingsManager.Default.MachineStudio.SynchronizationModule.RemoteSQLiteFile))
+ {
+ SlaveDBFile = SettingsManager.Default.MachineStudio.SynchronizationModule.RemoteSQLiteFile;
+ SlaveDBName = Path.GetFileName(SlaveDBFile);
+ }
+ }
+
+ #endregion
+
+ #region Commands
+
+ /// <summary>
+ /// Gets or sets the back command.
+ /// </summary>
+ public RelayCommand BackCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the browse master database command.
+ /// </summary>
+ public RelayCommand BrowseMasterDBCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the browse slave database command.
+ /// </summary>
+ public RelayCommand BrowseSlaveDBCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the compare command.
+ /// </summary>
+ public RelayCommand CompareCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the commit command.
+ /// </summary>
+ public RelayCommand CommitCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the commit all command.
+ /// </summary>
+ public RelayCommand CommitAllCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the clean command.
+ /// </summary>
+ public RelayCommand CleanCommand { get; set; }
+
+ #endregion
+
+ #region Properties
+
+ private ObservableCollection<Diff> _differences;
+ /// <summary>
+ /// Gets or sets the differences.
+ /// </summary>
+ public ObservableCollection<Diff> Differences
+ {
+ get { return _differences; }
+ set { _differences = value; RaisePropertyChanged(nameof(Differences)); }
+ }
+
+ private Diff _selectedDifference;
+ /// <summary>
+ /// Gets or sets the selected difference.
+ /// </summary>
+ public Diff SelectedDifference
+ {
+ get { return _selectedDifference; }
+ set { _selectedDifference = value; RaisePropertyChanged(nameof(SelectedDifference)); InvalidateRelayCommands(); }
+ }
+
+ private String _slaveDBName;
+ /// <summary>
+ /// Gets or sets the name of the slave database.
+ /// </summary>
+ public String SlaveDBName
+ {
+ get { return _slaveDBName; }
+ set { _slaveDBName = value; RaisePropertyChanged(nameof(SlaveDBName)); }
+ }
+
+ private Machine _selectedMachine;
+ /// <summary>
+ /// Gets or sets the selected machine.
+ /// </summary>
+ public Machine SelectedMachine
+ {
+ get { return _selectedMachine; }
+ set { _selectedMachine = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
+ }
+
+ /// <summary>
+ /// Gets or sets the slave database file.
+ /// </summary>
+ public String SlaveDBFile
+ {
+ get { return _slaveDBFile; }
+ set { _slaveDBFile = value; RaisePropertyChangedAuto(); }
+ }
+ #endregion
+
+ #region Private Methods
+
+ private async void CleanSlave()
+ {
+ if (_notification.ShowQuestion("Are you sure you want to erase all data on slave database?"))
+ {
+ using (_notification.PushTaskItem("Clearing database..."))
+ {
+ try
+ {
+ _isWorking = true;
+ await Task.Factory.StartNew(() =>
+ {
+ SQLiteDataBase localDB = new SQLiteDataBase(SlaveDBFile);
+ localDB.LoadTables();
+ localDB.ClearDataBase();
+ try
+ {
+ localDB.Dispose();
+ }
+ catch{ }
+ });
+
+ Differences.Clear();
+ }
+ catch (Exception ex)
+ {
+ ShowError(LogManager.Log(ex).Message);
+ }
+ finally
+ {
+ _isWorking = false;
+ InvalidateRelayCommands();
+ SelectedDifference = null;
+ }
+ }
+ }
+ }
+
+ private void Compare()
+ {
+ Task.Factory.StartNew(() =>
+ {
+ using (_notification.PushTaskItem("Comparing Databases..."))
+ {
+ try
+ {
+ if (_comparer != null)
+ {
+ _comparer.Dispose();
+ }
+
+ _remoteDB = RemoteDB.CreateDefault();
+ _localDB = new LocalDB(SlaveDBFile);
+ _comparer = new RemoteDBComparer(_remoteDB, _localDB, SelectedMachine.SerialNumber);
+ _isWorking = true;
+ InvalidateRelayCommands();
+ Thread.Sleep(1500);
+ var diffs = _comparer.Compare();
+ Differences = new ObservableCollection<Diff>(diffs);
+
+ if (diffs.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() > 0)
+ {
+ ShowInfo("Found " + Differences.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() + " differences.");
+ }
+ else
+ {
+ ShowInfo("The master and slave databases are synchronized.");
+ }
+ }
+ catch (Exception ex)
+ {
+ ShowError(ex.Message);
+ }
+ finally
+ {
+ _isWorking = false;
+ SelectedDifference = null;
+ InvalidateRelayCommands();
+
+ SettingsManager.Default.MachineStudio.SynchronizationModule.RemoteSQLiteFile = SlaveDBFile;
+ SettingsManager.SaveDefaultSettings();
+ }
+ }
+ });
+ }
+
+ private void Commit()
+ {
+ Task.Factory.StartNew(() =>
+ {
+ using (_notification.PushTaskItem("Committing difference..."))
+ {
+ try
+ {
+ _isWorking = true;
+ InvalidateRelayCommands();
+ Thread.Sleep(1500);
+ SelectedDifference.Commit();
+ _remoteDB.SaveChanges();
+ _localDB.SaveChanges();
+
+ InvokeUINow(() => Differences.Remove(SelectedDifference));
+ }
+ catch (Exception ex)
+ {
+ ShowError(ex.Message);
+ }
+ finally
+ {
+ _isWorking = false;
+ SelectedDifference = null;
+ InvalidateRelayCommands();
+ }
+ }
+ });
+ }
+
+ private void CommitAll()
+ {
+ Task.Factory.StartNew(() =>
+ {
+ using (_notification.PushTaskItem("Committing all differences..."))
+ {
+ try
+ {
+ _isWorking = true;
+ InvalidateRelayCommands();
+ Thread.Sleep(1500);
+
+ for (int i = 0; i < Differences.Count; i++)
+ {
+ var diff = Differences[i];
+ using (_notification.PushTaskItem("Committing difference " + (Differences.IndexOf(diff) + 1) + "..."))
+ {
+ diff.Commit();
+ InvokeUINow(() => Differences.Remove(diff));
+ i--;
+ }
+ }
+
+ _remoteDB.SaveChanges();
+ _localDB.SaveChanges();
+ }
+ catch (DbEntityValidationException ex)
+ {
+ String message = "The following validation errors occurred while trying to update the database." + Environment.NewLine + Environment.NewLine;
+
+ foreach (var error in ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).ToList())
+ {
+ message += error.ErrorMessage + Environment.NewLine;
+ }
+
+ ShowError(message);
+ }
+ catch (Exception ex)
+ {
+ ShowError(ex.Message);
+ }
+ finally
+ {
+ _isWorking = false;
+ SelectedDifference = null;
+ InvalidateRelayCommands();
+ }
+ }
+ });
+ }
+
+ private void BrowseSlaveDB()
+ {
+ String file = BrowseForFilePath();
+ if (file != null)
+ {
+ SlaveDBFile = file;
+ SlaveDBName = Path.GetFileName(file);
+ InvalidateRelayCommands();
+ }
+ }
+
+ private String BrowseForFilePath()
+ {
+ OpenFileDialog dlg = new OpenFileDialog();
+ dlg.Title = "Select SQLite Database File";
+ dlg.Filter = "SQLite Database|*.db";
+ if (dlg.ShowDialog().Value)
+ {
+ return dlg.FileName;
+ }
+ return null;
+ }
+
+ private void ShowError(String message)
+ {
+ InvokeUINow(() => _notification.ShowError(message));
+ }
+
+ private void ShowInfo(String message)
+ {
+ InvokeUINow(() => _notification.ShowInfo(message));
+ }
+
+ public Task<bool> OnShutdownRequest()
+ {
+ if (_comparer != null)
+ {
+ _comparer.Dispose();
+ }
+ return Task.FromResult(true);
+ }
+
+ #endregion
+ }
+}