diff options
| author | Roy Ben-Shabat <Roy@Twine-s.com> | 2017-11-26 17:23:38 +0200 |
|---|---|---|
| committer | Roy Ben-Shabat <Roy@Twine-s.com> | 2017-11-26 17:23:38 +0200 |
| commit | 262c1efc9d53b8b70fe677dc7d2d173896ffcee8 (patch) | |
| tree | e404dc914bee89101f50963ff018225c897244fd /Software | |
| parent | 0a8af16821cfbb52fe02881921695d378f6578d7 (diff) | |
| download | Tango-262c1efc9d53b8b70fe677dc7d2d173896ffcee8.tar.gz Tango-262c1efc9d53b8b70fe677dc7d2d173896ffcee8.zip | |
Implemented Tango SQLite DB Synchronizer.
Diffstat (limited to 'Software')
20 files changed, 814 insertions, 13 deletions
diff --git a/Software/Visual_Studio/Tango.Core/ExtensionMethods/DateTimeExtensions.cs b/Software/Visual_Studio/Tango.Core/ExtensionMethods/DateTimeExtensions.cs new file mode 100644 index 000000000..2c413b787 --- /dev/null +++ b/Software/Visual_Studio/Tango.Core/ExtensionMethods/DateTimeExtensions.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +public static class DateTimeExtensions +{ + /// <summary> + /// Converts the DateTime instance to an accurate time string. + /// </summary> + /// <param name="date">The date.</param> + /// <returns></returns> + public static String ToTimeString(this DateTime date) + { + return date.ToString("HH:mm:ss.ff"); + } +} diff --git a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj index 7b1074a3d..5963a7cc6 100644 --- a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj +++ b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj @@ -52,6 +52,7 @@ <Link>GlobalVersionInfo.cs</Link> </Compile> <Compile Include="ConcurrentList.cs" /> + <Compile Include="ExtensionMethods\DateTimeExtensions.cs" /> <Compile Include="ExtensionMethods\DependencyObjectExtensions.cs" /> <Compile Include="ExtensionMethods\EnumExtensions.cs" /> <Compile Include="ExtensionMethods\IMessageExtensions.cs" /> diff --git a/Software/Visual_Studio/Tango.Logging/ExceptionLogItem.cs b/Software/Visual_Studio/Tango.Logging/ExceptionLogItem.cs index de526f954..acebc2856 100644 --- a/Software/Visual_Studio/Tango.Logging/ExceptionLogItem.cs +++ b/Software/Visual_Studio/Tango.Logging/ExceptionLogItem.cs @@ -23,6 +23,15 @@ namespace Tango.Logging public String Description { get; set; } /// <summary> + /// Gets the log message. + /// </summary> + /// <returns></returns> + public override string GetMessage() + { + return InnerException.Message; + } + + /// <summary> /// Returns a formatted string of the log item. /// </summary> public override string ToString() diff --git a/Software/Visual_Studio/Tango.Logging/LogItemBase.cs b/Software/Visual_Studio/Tango.Logging/LogItemBase.cs index 38e78d3ac..aab78c63d 100644 --- a/Software/Visual_Studio/Tango.Logging/LogItemBase.cs +++ b/Software/Visual_Studio/Tango.Logging/LogItemBase.cs @@ -32,6 +32,12 @@ namespace Tango.Logging public DateTime TimeStamp { get; set; } /// <summary> + /// Gets the log message. + /// </summary> + /// <returns></returns> + public abstract String GetMessage(); + + /// <summary> /// Returns a formatted string of the log item. /// </summary> public abstract override String ToString(); diff --git a/Software/Visual_Studio/Tango.Logging/MessageLogItem.cs b/Software/Visual_Studio/Tango.Logging/MessageLogItem.cs index 44cb2c575..445706779 100644 --- a/Software/Visual_Studio/Tango.Logging/MessageLogItem.cs +++ b/Software/Visual_Studio/Tango.Logging/MessageLogItem.cs @@ -18,6 +18,15 @@ namespace Tango.Logging public String Message { get; set; } /// <summary> + /// Gets the log message. + /// </summary> + /// <returns></returns> + public override string GetMessage() + { + return Message; + } + + /// <summary> /// Returns a formatted string of the log item. /// </summary> public override string ToString() diff --git a/Software/Visual_Studio/Tango.SharedUI/ExtendedObject.cs b/Software/Visual_Studio/Tango.SharedUI/ExtendedObject.cs index 8e00f2e61..9068fc883 100644 --- a/Software/Visual_Studio/Tango.SharedUI/ExtendedObject.cs +++ b/Software/Visual_Studio/Tango.SharedUI/ExtendedObject.cs @@ -49,5 +49,23 @@ namespace Tango.SharedUI } })); } + + /// <summary> + /// Invokes the specified action on the UI Thread. + /// </summary> + /// <param name="action">The action.</param> + protected virtual void InvokeUI(Action action) + { + Application.Current.Dispatcher.BeginInvoke(action); + } + + /// <summary> + /// Invokes the specified action on the UI Thread. + /// </summary> + /// <param name="action">The action.</param> + protected virtual void InvokeUINow(Action action) + { + Application.Current.Dispatcher.Invoke(action); + } } } diff --git a/Software/Visual_Studio/Tango.Synchronization/ISqlDataBase.cs b/Software/Visual_Studio/Tango.Synchronization/ISqlDataBase.cs index f087dfd15..fa4e018be 100644 --- a/Software/Visual_Studio/Tango.Synchronization/ISqlDataBase.cs +++ b/Software/Visual_Studio/Tango.Synchronization/ISqlDataBase.cs @@ -24,6 +24,11 @@ namespace Tango.Synchronization List<DataTable> Tables { get; } /// <summary> + /// Loads the tables (Must be done before any synchronization). + /// </summary> + void LoadTables(); + + /// <summary> /// Clones a table from another database into this database. /// </summary> /// <param name="otherDB">The other database.</param> diff --git a/Software/Visual_Studio/Tango.Synchronization/SqlDataBaseComparer.cs b/Software/Visual_Studio/Tango.Synchronization/SqlDataBaseComparer.cs index 07c94045b..980a23f1e 100644 --- a/Software/Visual_Studio/Tango.Synchronization/SqlDataBaseComparer.cs +++ b/Software/Visual_Studio/Tango.Synchronization/SqlDataBaseComparer.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Data; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Logging; namespace Tango.Synchronization { @@ -40,28 +42,47 @@ namespace Tango.Synchronization /// <returns></returns> public List<SqlDiff> Compare() { + LogManager.Log("Comparing databases " + Path.GetFileName(MasterSQL.Source) + " <=> " + Path.GetFileName(SlaveSQL.Source)); + + LogManager.Log("Loading master tables..."); + MasterSQL.LoadTables(); + + LogManager.Log("Loading slave tables..."); + SlaveSQL.LoadTables(); + List<SqlDiff> diffs = new List<SqlDiff>(); foreach (var masterTable in MasterSQL.Tables) { + LogManager.Log("Comparing table " + masterTable.TableName); + LogManager.Log("Searching table " + masterTable.TableName + " on slave..."); + var slaveTable = SlaveSQL.Tables.SingleOrDefault(x => x.TableName == masterTable.TableName); //Get matching slave table on slave db. if (slaveTable == null) //Table not found on slave. { + LogManager.Log("Table not found on slave, adding difference."); //Clone table from slave db to master db including records! diffs.Add(new SqlDiff(SqlDiffAction.AddTableToSlave, String.Format("Add table {0} to slave", masterTable.TableName), () => SlaveSQL.CloneTableFrom(MasterSQL, masterTable), SlaveSQL.GetCloneTableFromCommand(MasterSQL, masterTable))); continue; } + LogManager.Log("Table found, comparing columns..."); + foreach (DataColumn masterColumn in masterTable.Columns) { + LogManager.Log("Searching for column " + masterColumn.ColumnName + " on slave..."); + var slaveColumn = slaveTable.Columns[masterColumn.ColumnName]; //Get matching slave column on slave table. if (slaveColumn == null) //Slave column not found. { + LogManager.Log("Column not found on slave, adding difference."); //Add column to slave table. diffs.Add(new SqlDiff(SqlDiffAction.AddColumnToSlave, String.Format("Add column {0} to slave table", masterColumn.ColumnName), () => SlaveSQL.AddColumn(masterTable, masterColumn), SlaveSQL.GetAddColumnCommand(masterTable, masterColumn))); } + + LogManager.Log("Column found."); } List<DataRow> addToSlave = new List<DataRow>(); @@ -69,46 +90,76 @@ namespace Tango.Synchronization List<DataRow> addToMaster = new List<DataRow>(); List<DataRow> updateMaster = new List<DataRow>(); + LogManager.Log("Comparing rows..."); + + int count = 0; + foreach (DataRow masterRow in masterTable.Rows) { + LogManager.Log("Comparing row " + count++); + String guid = masterRow.Field<String>(Constants.GUID); + LogManager.Log("Searching for row with GUID " + guid + " on slave table..."); + //Get Matching slave row. DataRow slaveRow = slaveTable.AsEnumerable().SingleOrDefault(x => x.Field<String>(Constants.GUID) == guid); if (slaveRow != null) { + LogManager.Log("Slave row found, comparing dates..."); + DateTime masterDate = masterRow.Field<DateTime>(Constants.LAST_UPDATED); DateTime slaveDate = slaveRow.Field<DateTime>(Constants.LAST_UPDATED); if (masterDate > slaveDate) { + LogManager.Log("Master => Slave Update " + masterDate.ToSQLiteDateString() + ", adding difference."); updateSlave.Add(masterRow); } else if (slaveDate > masterDate) { + LogManager.Log("Master <= Slave Update " + masterDate.ToSQLiteDateString() + ", adding difference."); updateMaster.Add(slaveRow); } + else + { + LogManager.Log("Master <=> Slave No Update."); + } } else { + LogManager.Log("Slave row not found, adding difference."); addToSlave.Add(masterRow); } } + LogManager.Log("Done comparing rows..."); + + LogManager.Log("Searching for missing rows on master..."); + foreach (DataRow slaveRow in slaveTable.Rows) { String guid = slaveRow.Field<String>(Constants.GUID); + LogManager.Log("Searching for row with GUID " + guid + " on master table..."); + //Get Matching slave row. DataRow masterRow = masterTable.AsEnumerable().SingleOrDefault(x => x.Field<String>(Constants.GUID) == guid); if (masterRow == null) { + LogManager.Log("Master row not found, adding difference."); addToMaster.Add(slaveRow); } + else + { + LogManager.Log("Master row found."); + } } + LogManager.Log("Done searching for missing rows on master..."); + foreach (var row in addToSlave) { diffs.Add(new SqlDiff(SqlDiffAction.AddRowToSlave, String.Format("Add row to slave table {0}", slaveTable.TableName), () => SlaveSQL.AddRow(slaveTable, row), SlaveSQL.GetAddRowCommand(slaveTable, row))); @@ -128,8 +179,12 @@ namespace Tango.Synchronization { diffs.Add(new SqlDiff(SqlDiffAction.UpdateRowInMaster, String.Format("Update row in master table {0}", masterTable.TableName), () => MasterSQL.UpdateRow(masterTable, row), MasterSQL.GetUpdateRowCommand(masterTable, row))); } + + LogManager.Log("Done comparing table " + masterTable.TableName); } + LogManager.Log("Databases comparison completed."); + return diffs; } diff --git a/Software/Visual_Studio/Tango.Synchronization/SqliteDataBase.cs b/Software/Visual_Studio/Tango.Synchronization/SqliteDataBase.cs index 3802232e0..75259fc28 100644 --- a/Software/Visual_Studio/Tango.Synchronization/SqliteDataBase.cs +++ b/Software/Visual_Studio/Tango.Synchronization/SqliteDataBase.cs @@ -43,14 +43,12 @@ namespace Tango.Synchronization )); _connection.Open(); - - InitializeTables(); } /// <summary> - /// Initializes the tables. + /// Loads the tables (Must be done before any synchronization). /// </summary> - private void InitializeTables() + public void LoadTables() { Tables = new List<DataTable>(); diff --git a/Software/Visual_Studio/Tango.Synchronization/Tango.Synchronization.csproj b/Software/Visual_Studio/Tango.Synchronization/Tango.Synchronization.csproj index b702c5335..6b19b8521 100644 --- a/Software/Visual_Studio/Tango.Synchronization/Tango.Synchronization.csproj +++ b/Software/Visual_Studio/Tango.Synchronization/Tango.Synchronization.csproj @@ -74,6 +74,12 @@ <None Include="App.config" /> <None Include="packages.config" /> </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Tango.Logging\Tango.Logging.csproj"> + <Project>{bc932dbd-7cdb-488c-99e4-f02cf441f55e}</Project> + <Name>Tango.Logging</Name> + </ProjectReference> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="..\packages\System.Data.SQLite.Core.1.0.106.0\build\net45\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.106.0\build\net45\System.Data.SQLite.Core.targets')" /> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> diff --git a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/App.xaml b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/App.xaml index 19bd64b04..c4d813ee4 100644 --- a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/App.xaml +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/App.xaml @@ -1,9 +1,26 @@ <Application x:Class="Tango.Synchronization.UI.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:vm="clr-namespace:Tango.Synchronization.UI.ViewModels" xmlns:local="clr-namespace:Tango.Synchronization.UI" StartupUri="MainWindow.xaml"> <Application.Resources> - + <ResourceDictionary> + <ResourceDictionary.MergedDictionaries> + <!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! --> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" /> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" /> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" /> + <!-- Accent and AppTheme setting --> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Steel.xaml" /> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" /> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatButton.xaml" /> + + <!--View Models--> + <ResourceDictionary> + <vm:MainViewVM x:Key="MainViewVM" /> + </ResourceDictionary> + </ResourceDictionary.MergedDictionaries> + </ResourceDictionary> </Application.Resources> </Application> diff --git a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Images/server.png b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Images/server.png Binary files differnew file mode 100644 index 000000000..3f856bfb8 --- /dev/null +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Images/server.png diff --git a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/MainWindow.xaml b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/MainWindow.xaml index c52dbc88b..41dd9d9e3 100644 --- a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/MainWindow.xaml +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/MainWindow.xaml @@ -1,12 +1,17 @@ -<Window x:Class="Tango.Synchronization.UI.MainWindow" +<mahapps:MetroWindow x:Class="Tango.Synchronization.UI.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:System="clr-namespace:System;assembly=mscorlib" + xmlns:converters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI" + xmlns:fa="http://schemas.fontawesome.io/icons/" xmlns:local="clr-namespace:Tango.Synchronization.UI" + xmlns:views="clr-namespace:Tango.Synchronization.UI.Views" mc:Ignorable="d" - Title="MainWindow" Height="350" Width="525"> + Title="Tango DataBase Synchronization Utility" Height="600" Width="1000" TitleCaps="False" BorderBrush="Gray" BorderThickness="1" WindowStartupLocation="CenterScreen" Background="#202020" Foreground="Gainsboro" DataContext="{Binding RelativeSource={RelativeSource Self}}"> <Grid> - + <views:MainView DataContext="{StaticResource MainViewVM}"></views:MainView> </Grid> -</Window> +</mahapps:MetroWindow> diff --git a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/MainWindow.xaml.cs b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/MainWindow.xaml.cs index cf4ebe4fa..6544b1f28 100644 --- a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/MainWindow.xaml.cs +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/MainWindow.xaml.cs @@ -1,4 +1,5 @@ -using System; +using MahApps.Metro.Controls; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -18,7 +19,7 @@ namespace Tango.Synchronization.UI /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> - public partial class MainWindow : Window + public partial class MainWindow : MetroWindow { public MainWindow() { diff --git a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Tango.Synchronization.UI.csproj b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Tango.Synchronization.UI.csproj index fa9da15f8..9cd8db74a 100644 --- a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Tango.Synchronization.UI.csproj +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Tango.Synchronization.UI.csproj @@ -36,8 +36,17 @@ <ApplicationIcon>server.ico</ApplicationIcon> </PropertyGroup> <ItemGroup> + <Reference Include="FontAwesome.WPF, Version=4.7.0.37774, Culture=neutral, PublicKeyToken=0758b07a11a4f466, processorArchitecture=MSIL"> + <HintPath>..\..\packages\FontAwesome.WPF.4.7.0.9\lib\net40\FontAwesome.WPF.dll</HintPath> + </Reference> + <Reference Include="MahApps.Metro, Version=1.5.0.23, Culture=neutral, PublicKeyToken=f4fb5a3c4d1e5b4f, processorArchitecture=MSIL"> + <HintPath>..\..\packages\MahApps.Metro.1.5.0\lib\net45\MahApps.Metro.dll</HintPath> + </Reference> <Reference Include="System" /> <Reference Include="System.Data" /> + <Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> + <HintPath>..\..\packages\MahApps.Metro.1.5.0\lib\net45\System.Windows.Interactivity.dll</HintPath> + </Reference> <Reference Include="System.Xml" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Core" /> @@ -56,6 +65,10 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </ApplicationDefinition> + <Compile Include="ViewModels\MainViewVM.cs" /> + <Compile Include="Views\MainView.xaml.cs"> + <DependentUpon>MainView.xaml</DependentUpon> + </Compile> <Page Include="MainWindow.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> @@ -71,6 +84,10 @@ <DependentUpon>MainWindow.xaml</DependentUpon> <SubType>Code</SubType> </Compile> + <Page Include="Views\MainView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> </ItemGroup> <ItemGroup> <Compile Include="Properties\AssemblyInfo.cs"> @@ -91,6 +108,7 @@ <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>Resources.Designer.cs</LastGenOutput> </EmbeddedResource> + <None Include="packages.config" /> <None Include="Properties\Settings.settings"> <Generator>SettingsSingleFileGenerator</Generator> <LastGenOutput>Settings.Designer.cs</LastGenOutput> @@ -102,6 +120,27 @@ <ItemGroup> <Resource Include="server.ico" /> </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\Tango.Core\Tango.Core.csproj"> + <Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project> + <Name>Tango.Core</Name> + </ProjectReference> + <ProjectReference Include="..\..\Tango.Logging\Tango.Logging.csproj"> + <Project>{bc932dbd-7cdb-488c-99e4-f02cf441f55e}</Project> + <Name>Tango.Logging</Name> + </ProjectReference> + <ProjectReference Include="..\..\Tango.SharedUI\Tango.SharedUI.csproj"> + <Project>{ac489889-6e50-4f16-9dba-ff4c6f9ec72b}</Project> + <Name>Tango.SharedUI</Name> + </ProjectReference> + <ProjectReference Include="..\..\Tango.Synchronization\Tango.Synchronization.csproj"> + <Project>{7ada4e86-cad7-4968-a210-3a8a9e5153ab}</Project> + <Name>Tango.Synchronization</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Resource Include="Images\server.png" /> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <PropertyGroup> <PostBuildEvent>$(TargetDir)linkgen.exe -s "$(TargetPath)" -d "$(TargetDir)Utilities\DB Sync.lnk"</PostBuildEvent> diff --git a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/ViewModels/MainViewVM.cs b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/ViewModels/MainViewVM.cs new file mode 100644 index 000000000..d388d761f --- /dev/null +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/ViewModels/MainViewVM.cs @@ -0,0 +1,331 @@ +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 System.Windows; +using Tango.Logging; +using Tango.SharedUI; +using Tango.SharedUI.Commands; + +namespace Tango.Synchronization.UI.ViewModels +{ + public class MainViewVM : ExtendedObject + { + private String _masterDBFile; + private String _slaveDBFile; + private SqlDataBaseComparer _comparer; + + #region Properties + + private bool _isBusy; + /// <summary> + /// Gets or sets a value indicating whether an operation is in progress. + /// </summary> + public bool IsBusy + { + get { return _isBusy; } + set { _isBusy = value; RaisePropertyChanged(nameof(IsBusy)); } + } + + private String _status; + /// <summary> + /// Gets or sets the current operation status. + /// </summary> + public String Status + { + get { return _status; } + set { _status = value; RaisePropertyChanged(nameof(Status)); } + } + + 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)); } + } + + private ObservableCollection<SqlDiff> _differences; + /// <summary> + /// Gets or sets the differences. + /// </summary> + public ObservableCollection<SqlDiff> Differences + { + get { return _differences; } + set { _differences = value; RaisePropertyChanged(nameof(Differences)); } + } + + private SqlDiff _selectedDifference; + /// <summary> + /// Gets or sets the selected difference. + /// </summary> + public SqlDiff 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)); } + } + + #endregion + + #region Commands + + /// <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; } + + #endregion + + #region Constructors + + /// <summary> + /// Initializes a new instance of the <see cref="MainViewVM"/> class. + /// </summary> + public MainViewVM() + { + MainViewLogger logger = new MainViewLogger(); + logger.NewLog += (output) => + { + Log += output + Environment.NewLine; + }; + + LogManager.RegisterLogger(logger); + + LogManager.Log("DB Synchronizer Started!"); + + Differences = new ObservableCollection<SqlDiff>(); + + BrowseMasterDBCommand = new RelayCommand(BrowseMasterDB); + BrowseSlaveDBCommand = new RelayCommand(BrowseSlaveDB); + CompareCommand = new RelayCommand(Compare, (x) => _masterDBFile != null && _slaveDBFile != null); + CommitCommand = new RelayCommand(Commit, (x) => SelectedDifference != null); + CommitAllCommand = new RelayCommand(CommitAll, (x) => Differences.Count > 0); + + Task.Factory.StartNew(() => + { + OpenStatus("Loading components, please wait..."); + Thread.Sleep(1000); + CloseStatus(); + }); + } + + #endregion + + #region Private Methods + + private void Compare() + { + _comparer = new SqlDataBaseComparer(new SqliteDataBase(_masterDBFile), new SqliteDataBase(_slaveDBFile)); + + OpenStatus("Comparing DataBase..."); + + Task.Factory.StartNew(() => + { + try + { + Thread.Sleep(1500); + var diffs = _comparer.Compare(); + Differences = new ObservableCollection<SqlDiff>(diffs); + } + catch (Exception ex) + { + ShowError(ex.Message); + } + finally + { + SelectedDifference = null; + InvalidateRelayCommands(); + CloseStatus(); + } + }); + } + + private void Commit() + { + OpenStatus("Committing difference..."); + + Task.Factory.StartNew(() => + { + try + { + Thread.Sleep(1500); + SelectedDifference.Commit(); + + InvokeUINow(() => Differences.Remove(SelectedDifference)); + } + catch (Exception ex) + { + ShowError(ex.Message); + } + finally + { + SelectedDifference = null; + InvalidateRelayCommands(); + CloseStatus(); + } + }); + } + + private void CommitAll() + { + OpenStatus("Committing all differences..."); + + Task.Factory.StartNew(() => + { + try + { + Thread.Sleep(1500); + + for (int i = 0; i < Differences.Count; i++) + { + var diff = Differences[i]; + OpenStatus("Committing difference " + (Differences.IndexOf(diff) + 1) + "..."); + diff.Commit(); + InvokeUINow(() => Differences.Remove(diff)); + i--; + } + } + catch (Exception ex) + { + ShowError(ex.Message); + } + finally + { + SelectedDifference = null; + InvalidateRelayCommands(); + CloseStatus(); + } + }); + } + + 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 OpenStatus(String status) + { + IsBusy = true; + Status = status; + } + + private void CloseStatus() + { + IsBusy = false; + } + + private void ShowError(String message) + { + MessageBox.Show(message, "Tango", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.No, MessageBoxOptions.DefaultDesktopOnly); + } + + private void ShowInfo(String message) + { + MessageBox.Show(message, "Tango", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.No, MessageBoxOptions.DefaultDesktopOnly); + } + #endregion + + #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/Utilities/Tango.Synchronization.UI/Views/MainView.xaml b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Views/MainView.xaml new file mode 100644 index 000000000..bc927399b --- /dev/null +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Views/MainView.xaml @@ -0,0 +1,251 @@ +<UserControl x:Class="Tango.Synchronization.UI.Views.MainView" + 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:mahapps="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:System="clr-namespace:System;assembly=mscorlib" + xmlns:converters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI" + xmlns:fa="http://schemas.fontawesome.io/icons/" + xmlns:local="clr-namespace:Tango.Synchronization.UI.Views" + mc:Ignorable="d" + d:DesignHeight="600" d:DesignWidth="1000" Background="#202020"> + + <UserControl.Resources> + <VisualBrush x:Key="badgeBackground"> + <VisualBrush.Visual> + <Border> + <Path Stretch="Fill" RenderTransformOrigin="0.5,0.5"> + <Path.RenderTransform> + <ScaleTransform ScaleX="-1"></ScaleTransform> + </Path.RenderTransform> + <Path.Fill> + <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> + <GradientStop Color="#202020" Offset="0.4" /> + <GradientStop Color="#FF4E4E4E" Offset="1"/> + </LinearGradientBrush> + </Path.Fill> + <Path.Data> + <PathGeometry Figures="M 53.868 43.913 H 19.511 c -0.444 0 -0.875 -0.151 -1.221 -0.428 L 0.734 29.439 c -0.978 -0.783 -0.978 -2.271 0 -3.053 L 18.29 12.341 c 0.347 -0.277 0.777 -0.428 1.221 -0.428 h 34.356 c 1.081 0 1.958 0.877 1.958 1.958 v 28.084 c 0 1.081 -0.876 1.958 -1.957 1.958 z" FillRule="NonZero"/> + </Path.Data> + </Path> + </Border> + </VisualBrush.Visual> + </VisualBrush> + + <converters:BooleanInverseConverter x:Key="BooleanInverseConverter"></converters:BooleanInverseConverter> + </UserControl.Resources> + + <Grid> + <Grid.Style> + <Style TargetType="Grid"> + <Style.Triggers> + <DataTrigger Binding="{Binding IsBusy}" Value="True"> + <Setter Property="Cursor" Value="AppStarting"></Setter> + </DataTrigger> + <DataTrigger Binding="{Binding IsBusy}" Value="False"> + <Setter Property="Cursor" Value="Arrow"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </Grid.Style> + + <Grid.RowDefinitions> + <RowDefinition Height="100"/> + <RowDefinition Height="1*"/> + </Grid.RowDefinitions> + + <Grid> + <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="20 15"> + <Image Source="/Images/server.png" RenderOptions.BitmapScalingMode="Fant"></Image> + <TextBlock Text="Tango DB Synchronizer" VerticalAlignment="Center" Margin="20 0 0 0" FontSize="36" Foreground="Silver"> + <TextBlock.Effect> + <DropShadowEffect/> + </TextBlock.Effect> + </TextBlock> + </StackPanel> + + <Rectangle VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Margin="20 0 50 0" Stroke="DimGray" StrokeThickness="1" StrokeDashArray="5"></Rectangle> + </Grid> + + <Grid Grid.Row="1" Margin="10"> + <Grid.RowDefinitions> + <RowDefinition Height="218*"/> + <RowDefinition Height="5"/> + <RowDefinition Height="151*"/> + </Grid.RowDefinitions> + + <Grid IsEnabled="{Binding IsBusy,Converter={StaticResource BooleanInverseConverter}}"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="600"/> + <ColumnDefinition Width="1*"/> + </Grid.ColumnDefinitions> + + <Grid Margin="5"> + <GroupBox Header="Compare"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="57*"/> + <RowDefinition Height="50"/> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition MaxWidth="250" /> + <ColumnDefinition/> + </Grid.ColumnDefinitions> + + <Grid> + <Grid.RowDefinitions> + <RowDefinition/> + <RowDefinition/> + </Grid.RowDefinitions> + + <StackPanel HorizontalAlignment="Left" VerticalAlignment="Center" Margin="20 0 0 0"> + <TextBlock Background="{StaticResource badgeBackground}" Padding="5" Width="200" FontSize="11" HorizontalAlignment="Left">Master DataBase</TextBlock> + <StackPanel Orientation="Horizontal" Margin="0 5 0 0"> + <TextBox IsReadOnly="True" Width="130" BorderBrush="#4E4E4E" Text="{Binding MasterDBName}"></TextBox> + <Button Margin="5 0 0 0" Cursor="Hand" VerticalAlignment="Center" Style="{DynamicResource MetroCircleButtonStyle}" mahapps:ButtonHelper.PreserveTextCase="True" BorderThickness="0" Command="{Binding BrowseMasterDBCommand}"> + <fa:ImageAwesome Icon="FolderOpen" Width="24" Foreground="Gray"></fa:ImageAwesome> + </Button> + </StackPanel> + </StackPanel> + + <StackPanel HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="1" Margin="20 0 0 0"> + <TextBlock Background="{StaticResource badgeBackground}" Padding="5" Width="200" FontSize="11" HorizontalAlignment="Left">Slave DataBase</TextBlock> + <StackPanel Orientation="Horizontal" Margin="0 5 0 0"> + <TextBox IsReadOnly="True" Width="130" BorderBrush="#4E4E4E" Text="{Binding SlaveDBName}"></TextBox> + <Button Margin="5 0 0 0" Cursor="Hand" VerticalAlignment="Center" Style="{DynamicResource MetroCircleButtonStyle}" mahapps:ButtonHelper.PreserveTextCase="True" BorderThickness="0" Command="{Binding BrowseSlaveDBCommand}"> + <fa:ImageAwesome Icon="FolderOpen" Width="24" Foreground="Gray"></fa:ImageAwesome> + </Button> + </StackPanel> + </StackPanel> + </Grid> + + <Button Margin="20 10" Grid.Row="1" Width="130" HorizontalAlignment="Left" Style="{StaticResource AccentedSquareButtonStyle}" mahapps:ButtonHelper.PreserveTextCase="True" BorderThickness="0" Command="{Binding CompareCommand}"> + <StackPanel Orientation="Horizontal"> + <TextBlock VerticalAlignment="Center" Margin="10 0 10 0">COMPARE</TextBlock> + <fa:ImageAwesome Icon="LongArrowRight" VerticalAlignment="Center" Foreground="{Binding RelativeSource={RelativeSource AncestorType=Button},Path=Foreground}" Width="16"></fa:ImageAwesome> + </StackPanel> + </Button> + + <Grid Grid.Column="1" Grid.RowSpan="2"> + <Grid.RowDefinitions> + <RowDefinition Height="20"/> + <RowDefinition Height="205*"/> + </Grid.RowDefinitions> + <TextBlock Margin="10 5 0 0">Differences</TextBlock> + <ListBox Margin="10" Background="#151515" Grid.Row="1" ItemsSource="{Binding Differences}" SelectedItem="{Binding SelectedDifference,Mode=TwoWay}"> + <ListBox.ItemTemplate> + <DataTemplate> + <Border Padding="10"> + <StackPanel Orientation="Horizontal"> + <fa:ImageAwesome Icon="Cog" Width="20" Height="20" Foreground="Gray"></fa:ImageAwesome> + <TextBlock VerticalAlignment="Center" Margin="10 0 0 0" Text="{Binding Description}"></TextBlock> + </StackPanel> + </Border> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + </Grid> + </Grid> + </GroupBox> + </Grid> + + <Grid Grid.Column="1"> + <GroupBox Header="Synchronize" Margin="10"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="1*"/> + <RowDefinition Height="50"/> + </Grid.RowDefinitions> + + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="20"/> + <RowDefinition Height="71*"/> + </Grid.RowDefinitions> + <TextBlock Margin="10 0 0 0" VerticalAlignment="Bottom">SQL Command</TextBlock> + <TextBox Margin="10" Foreground="Gray" FontSize="11" Text="{Binding SelectedDifference.Command,Mode=OneWay}" BorderThickness="0" IsReadOnly="True" Background="#151515" Padding="5" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" AcceptsReturn="True" Grid.Row="1"></TextBox> + </Grid> + + <Grid Grid.Row="1"> + <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> + <Button Margin="10 10" Grid.Row="1" Width="120" HorizontalAlignment="Right" Style="{StaticResource AccentedSquareButtonStyle}" mahapps:ButtonHelper.PreserveTextCase="True" BorderThickness="0" Command="{Binding CommitCommand}"> + <StackPanel Orientation="Horizontal"> + <TextBlock VerticalAlignment="Center" Margin="10 0 10 0">COMMIT</TextBlock> + <fa:ImageAwesome Icon="Bolt" VerticalAlignment="Center" Foreground="{Binding RelativeSource={RelativeSource AncestorType=Button},Path=Foreground}" Width="16"></fa:ImageAwesome> + </StackPanel> + </Button> + <Button Margin="10 10" Grid.Row="1" Width="120" HorizontalAlignment="Right" Style="{StaticResource AccentedSquareButtonStyle}" mahapps:ButtonHelper.PreserveTextCase="True" BorderThickness="0" Command="{Binding CommitAllCommand}"> + <StackPanel Orientation="Horizontal"> + <TextBlock VerticalAlignment="Center" Margin="10 0 10 0">COMMIT ALL</TextBlock> + <fa:ImageAwesome Icon="SortAmountAsc" VerticalAlignment="Center" Foreground="{Binding RelativeSource={RelativeSource AncestorType=Button},Path=Foreground}" Width="16"></fa:ImageAwesome> + </StackPanel> + </Button> + </StackPanel> + </Grid> + </Grid> + </GroupBox> + </Grid> + </Grid> + + <GridSplitter Margin="5 0 5 0" Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center"> + <GridSplitter.Background> + <LinearGradientBrush> + <GradientStop/> + <GradientStop Color="#FF848484" Offset="0.5"/> + <GradientStop Offset="1"/> + </LinearGradientBrush> + </GridSplitter.Background> + </GridSplitter> + + <Grid Grid.Row="2" Background="Black" Margin="10 5"> + + <TextBox Text="{Binding Log,Mode=OneWay}" Background="Transparent" IsReadOnly="True" Padding="5" BorderThickness="0" AcceptsReturn="True" VerticalScrollBarVisibility="Visible" FontSize="11" Foreground="Silver"> + + </TextBox> + + <Border HorizontalAlignment="Right" Margin="0 0 10 0" VerticalAlignment="Top" Width="300" Height="40" Padding="5" CornerRadius="0 0 30 30" BorderThickness="1 0 1 1" BorderBrush="DimGray"> + <Border.Style> + <Style TargetType="Border"> + <Setter Property="RenderTransform"> + <Setter.Value> + <ScaleTransform ScaleX="1" ScaleY="1"></ScaleTransform> + </Setter.Value> + </Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding IsBusy}" Value="True"> + <DataTrigger.EnterActions> + <BeginStoryboard> + <Storyboard> + <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" To="1" Duration="00:00:0.2"></DoubleAnimation> + </Storyboard> + </BeginStoryboard> + </DataTrigger.EnterActions> + <DataTrigger.ExitActions> + <BeginStoryboard> + <Storyboard> + <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" To="0" BeginTime="00:00:01" Duration="00:00:0.5"></DoubleAnimation> + </Storyboard> + </BeginStoryboard> + </DataTrigger.ExitActions> + </DataTrigger> + </Style.Triggers> + </Style> + </Border.Style> + <Border.Background> + <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> + <GradientStop Color="#FF272727"/> + <GradientStop Color="#FF777777" Offset="1"/> + </LinearGradientBrush> + </Border.Background> + + <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="20 0 0 0"> + <mahapps:ProgressRing Width="24" Height="24" Foreground="Gainsboro"></mahapps:ProgressRing> + <TextBlock Text="{Binding Status}" Foreground="Gainsboro" VerticalAlignment="Center" Margin="10 0 0 0"></TextBlock> + </StackPanel> + </Border> + + </Grid> + </Grid> + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Views/MainView.xaml.cs b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Views/MainView.xaml.cs new file mode 100644 index 000000000..5316ade21 --- /dev/null +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/Views/MainView.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.Synchronization.UI.Views +{ + /// <summary> + /// Interaction logic for MainView.xaml + /// </summary> + public partial class MainView : UserControl + { + public MainView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/packages.config b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/packages.config new file mode 100644 index 000000000..e3961b62a --- /dev/null +++ b/Software/Visual_Studio/Utilities/Tango.Synchronization.UI/packages.config @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="FontAwesome.WPF" version="4.7.0.9" targetFramework="net45" /> + <package id="MahApps.Metro" version="1.5.0" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/Software/Visual_Studio/Web/Tango.MachineService/packages.config b/Software/Visual_Studio/Web/Tango.MachineService/packages.config index 0b8287c43..80a6fd594 100644 --- a/Software/Visual_Studio/Web/Tango.MachineService/packages.config +++ b/Software/Visual_Studio/Web/Tango.MachineService/packages.config @@ -1,8 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="Antlr" version="3.4.1.9004" targetFramework="net45" /> - <package id="bootstrap" version="3.0.0" targetFramework="net45" /> - <package id="jQuery" version="1.10.2" targetFramework="net45" /> <package id="Microsoft.ApplicationInsights" version="2.2.0" targetFramework="net45" /> <package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.0.6" targetFramework="net45" /> <package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.2.0" targetFramework="net45" /> |
