diff options
| author | Avi Levkovich <avi@twine-s.com> | 2017-12-28 15:16:27 +0200 |
|---|---|---|
| committer | Avi Levkovich <avi@twine-s.com> | 2017-12-28 15:16:27 +0200 |
| commit | c056f991c0168f6449fac16c3b0cb165518c5e01 (patch) | |
| tree | e5c69bb1f3e03029991efe15e930af80ff6712db /Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels | |
| parent | 15799210aab388996f86808e000940093306ca41 (diff) | |
| parent | 2d3f4bfd4b265888933ad8a7e21e4dd80aa1eda2 (diff) | |
| download | Tango-c056f991c0168f6449fac16c3b0cb165518c5e01.tar.gz Tango-c056f991c0168f6449fac16c3b0cb165518c5e01.zip | |
MERGE.
Diffstat (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels')
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 + } +} |
