using Google.Protobuf; using Microsoft.Practices.ServiceLocation; 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.BL.Entities; 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 { /// /// Represents the 'Direct Synchronization' view model. /// /// public class DirectSynchronizationViewVM : ViewModel { private SyncNavigationManager _navigation; private String _slaveDBFile; private String _masterDBFile; private LocalDBComparer _comparer; private INotificationProvider _notification; private String _comparedSerialNumber; private MainViewVM _mainView; #region Constructors /// /// Initializes a new instance of the class. /// /// The application manager. /// The navigation. /// The notification. public DirectSynchronizationViewVM(IStudioApplicationManager applicationManager, SyncNavigationManager navigation, INotificationProvider notification) { ApplicationManager = applicationManager; _navigation = navigation; _notification = notification; BackCommand = new RelayCommand(() => _navigation.NavigateTo(NavigationView.MenuView)); Differences = new ObservableCollection(); CompareCommand = new RelayCommand(Compare, (x) => !IsWorking && SelectedMachine != null); CommitAllCommand = new RelayCommand(Synchronize, (x) => Differences.Count > 0 && !IsWorking && SelectedMachine != null); } #endregion #region Commands /// /// Gets or sets the back command. /// public RelayCommand BackCommand { get; set; } /// /// Gets or sets the browse master database command. /// public RelayCommand BrowseMasterDBCommand { get; set; } /// /// Gets or sets the browse slave database command. /// public RelayCommand BrowseSlaveDBCommand { get; set; } /// /// Gets or sets the compare command. /// public RelayCommand CompareCommand { get; set; } /// /// Gets or sets the commit command. /// public RelayCommand CommitCommand { get; set; } /// /// Gets or sets the commit all command. /// public RelayCommand CommitAllCommand { get; set; } /// /// Gets or sets the clean command. /// public RelayCommand CleanCommand { get; set; } #endregion #region Properties public IStudioApplicationManager ApplicationManager { get; set; } private bool _isWorking; /// /// Gets or sets a value indicating whether this instance is working. /// public bool IsWorking { get { return _isWorking; } set { _isWorking = value; RaisePropertyChangedAuto(); } } private bool _isClearMachine; /// /// Gets or sets a value indicating whether this instance is clear machine. /// public bool IsClearMachine { get { return _isClearMachine; } set { _isClearMachine = value; RaisePropertyChangedAuto(); } } private ObservableCollection _differences; /// /// Gets or sets the differences. /// public ObservableCollection Differences { get { return _differences; } set { _differences = value; RaisePropertyChanged(nameof(Differences)); } } private Diff _selectedDifference; /// /// Gets or sets the selected difference. /// public Diff SelectedDifference { get { return _selectedDifference; } set { _selectedDifference = value; RaisePropertyChanged(nameof(SelectedDifference)); InvalidateRelayCommands(); } } private Machine _selectedMachine; /// /// Gets or sets the selected machine. /// public Machine SelectedMachine { get { return _selectedMachine; } set { _selectedMachine = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } #endregion #region Event Handlers private void Comparer_Progress(object sender, string e) { if (_mainView == null) { _mainView = ServiceLocator.Current.GetInstance(); _mainView.Log = String.Empty; } _mainView.Log += ("[" + DateTime.Now.ToTimeString() + "] " + e + Environment.NewLine); } #endregion #region Private Methods /// /// Compares the selected machine against the remote database. /// private void Compare() { if (SelectedMachine.SerialNumber != ApplicationManager.ConnectedMachine.As().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(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)); _comparer.Progress += Comparer_Progress; var diffs = _comparer.Compare(); Differences = new ObservableCollection(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; } } }); } /// /// Synchronizes the selected machine with the remote database. /// 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(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; } } }); } /// /// Displays an error message. /// /// The message. private void ShowError(String message) { InvokeUINow(() => _notification.ShowError(message)); } /// /// Displays an information message. /// /// The message. private void ShowInfo(String message) { InvokeUINow(() => _notification.ShowInfo(message)); } #endregion } }