aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Embedded_SW/Embedded
ModeNameSize
-rw-r--r--.ccsproject591logstatsplain
d---------.config87logstatsplain
-rw-r--r--.cproject68854logstatsplain
-rw-r--r--.project1086logstatsplain
d---------.settings120logstatsplain
-rw-r--r--.xdchelp0logstatsplain
d---------Common219logstatsplain
d---------Communication282logstatsplain
-rw-r--r--DataDef.h17669logstatsplain
d---------Drivers473logstatsplain
-rw-r--r--Embedded parameters handling.txt6166logstatsplain
-rw-r--r--Embedded.cfg10696logstatsplain
-rw-r--r--Include.h1543logstatsplain
-rw-r--r--Main.c13977logstatsplain
d---------Modules345logstatsplain
d---------RemoteSystemsTempFiles36logstatsplain
-rw-r--r--Software Release Notes.txt33020logstatsplain
d---------StateMachines76logstatsplain
d---------debug_w_pmr429logstatsplain
-rw-r--r--embeddedparametersbuild.cs8825logstatsplain
d---------src111logstatsplain
d---------targetConfigs90logstatsplain
-rw-r--r--tm4c129xnczad.cmdpre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using remote = Tango.DAL.Remote.DB;
using local = Tango.DAL.Local.DB;
using System.Data.Entity;
using Tango.Logging;
using System.Collections;

namespace Tango.Synchronization.Remote
{
    /// <summary>
    /// Represents a Remote database to SQLite database comparison engine.
    /// </summary>
    /// <seealso cref="Tango.Synchronization.IDBComparer" />
    public class RemoteDBComparer : IDBComparer
    {
        private remote.RemoteDB _remoteDB;
        private local.LocalDB _localDB;
        private String _machineSerial;
        private List<Diff> _diffs;
        private LogManager LogManager = LogManager.Default;

        /// <summary>
        /// Used to notify about the comparer progress using text messages.
        /// </summary>
        public event EventHandler<string> Progress;

        /// <summary>
        /// Initializes a new instance of the <see cref="RemoteDBComparer"/> class.
        /// </summary>
        /// <param name="remoteDB">The remote database.</param>
        /// <param name="localDB">The local database.</param>
        /// <param name="machineSerial">The machine serial number.</param>
        public RemoteDBComparer(remote.RemoteDB remoteDB, local.LocalDB localDB, String machineSerial)
        {
            _machineSerial = machineSerial;
            _remoteDB = remoteDB;
            _localDB = localDB;
        }

        /// <summary>
        /// Performs a database comparison and returns a set of differences.
        /// </summary>
        /// <returns></returns>
        /// <exception cref="Tango.Synchronization.DataBaseComparisonException">Could not locate machine on remote server " + _machineSerial</exception>
        public List<Diff> Compare()
        {
            _diffs = new List<Diff>();

            OnProgress(LogManager.Log("Performing SQL Server <=> SQLite database comparison..."));

            OnProgress(LogManager.Log("Loading SQLite database to memory..."));

            OnProgress(LogManager.Log("Querying all local addresses..."));
            var local_addresses = _localDB.ADDRESSES.ToList();
            OnProgress(LogManager.Log("Querying all local configurations..."));
            var local_configurations = _localDB.CONFIGURATIONS.ToList();
            OnProgress(LogManager.Log("Querying all local configuration dispensers..."));
            var local_ids_packs = _localDB.IDS_PACKS.ToList();
            OnProgress(LogManager.Log("Querying all local contacts..."));
            var local_contacts = _localDB.CONTACTS.ToList();
            OnProgress(LogManager.Log("Querying all local machines..."));
            var local_machines = _localDB.MACHINES.Where(x => x.SERIAL_NUMBER == _machineSerial).ToList();
            OnProgress(LogManager.Log("Querying all local machines configurations..."));
            var local_machines_configurations = _localDB.MACHINES_CONFIGURATIONS.ToList();
            OnProgress(LogManager.Log("Querying all local machines events..."));
            var local_machines_events = _localDB.MACHINES_EVENTS.ToList();
            OnProgress(LogManager.Log("Querying all local organizations..."));
            var local_organizations = _localDB.ORGANIZATIONS.ToList();
            OnProgress(LogManager.Log("Querying all local users..."));
            var local_users = _localDB.USERS.ToList();
            OnProgress(LogManager.Log("Querying all local users roles..."));
            var local_users_roles = _localDB.USERS_ROLES.ToList();
            OnProgress(LogManager.Log("Querying all local jobs..."));
            var local_jobs = _localDB.JOBS.ToList();
            OnProgress(LogManager.Log("Querying all local job runs..."));
            var local_job_runs = _localDB.JOB_RUNS.ToList();
            OnProgress(LogManager.Log("Querying all local segments..."));
            var local_segments = _localDB.SEGMENTS.ToList();
            OnProgress(LogManager.Log("Querying all local brush stops..."));
            var local_brush_stops = _localDB.BRUSH_STOPS.ToList();
            //LogManager.Log("Querying all CATS...");
            //var local_cats = _localDB.CATS.ToList();

            OnProgress(LogManager.Log("Loading SQL Server database to memory..."));

            List<String> guids = new List<string>();

            OnProgress(LogManager.Log("Querying all remote machines..."));
            var remote_machines = _remoteDB.MACHINES.Where(x => x.SERIAL_NUMBER == _machineSerial).ToList();

            if (remote_machines.Count == 0)
            {
                throw LogManager.Log(new DataBaseComparisonException("Could not locate machine on remote server " + _machineSerial));
            }

            OnProgress(LogManager.Log("Querying all remote organizations..."));
            guids = remote_machines.Select(x => x.ORGANIZATION_GUID).ToList();
            var remote_organizations = _remoteDB.ORGANIZATIONS.Where(x => guids.Contains(x.GUID)).ToList();

            OnProgress(LogManager.Log("Querying all remote machines configurations..."));
            guids = remote_machines.Select(x => x.GUID).ToList();
            var remote_machines_configurations = _remoteDB.MACHINES_CONFIGURATIONS.Where(x => guids.Contains(x.MACHINE_GUID)).ToList();
            var remote_configurations = remote_machines_configurations.Select(x => x.CONFIGURATION).ToList();

            OnProgress(LogManager.Log("Querying all remote machines events..."));
            var remote_machines_events = _remoteDB.MACHINES_EVENTS.Where(x => guids.Contains(x.MACHINE_GUID)).ToList();

            OnProgress(LogManager.Log("Querying all remote users..."));
            guids = remote_organizations.Select(x => x.GUID).ToList();
            var remote_users = _remoteDB.USERS.Where(x => guids.Contains(x.ORGANIZATION_GUID)).ToList();

            OnProgress(LogManager.Log("Querying all remote users roles..."));
            guids = remote_users.Select(x => x.GUID).ToList();
            var remote_users_roles = _remoteDB.USERS_ROLES.Where(x => guids.Contains(x.USER_GUID)).ToList();

            OnProgress(LogManager.Log("Querying all remote addresses..."));
            var remote_addresses = remote_users.Select(x => x.ADDRESS).ToList();
            remote_addresses.AddRange(remote_machines.Select(x => x.ORGANIZATION).Select(x => x.ADDRESS));
            remote_addresses = remote_addresses.Distinct().ToList();

            OnProgress(LogManager.Log("Querying all remote contacts..."));
            var remote_contacts = remote_users.Select(x => x.CONTACT).ToList();
            remote_contacts.AddRange(remote_machines.Select(x => x.ORGANIZATION).Select(x => x.CONTACT));
            remote_contacts = remote_contacts.Distinct().ToList();

            OnProgress(LogManager.Log("Querying all remote IDS Packs..."));
            guids = remote_configurations.Select(x => x.GUID).ToList();
            var remote_ids_packs = _remoteDB.IDS_PACKS.Where(x => guids.Contains(x.CONFIGURATION_GUID)).ToList();

            OnProgress(LogManager.Log("Querying all remote dispenser types..."));
            guids = remote_ids_packs.Select(x => x.DISPENSER_TYPE_GUID).ToList();
            var remote_dispensers_types = _remoteDB.DISPENSER_TYPES.Where(x => guids.Contains(x.GUID)).ToList();

            OnProgress(LogManager.Log("Querying all remote cartridge types..."));
            guids = remote_ids_packs.Select(x => x.CARTRIDGE_TYPE_GUID).ToList();
            var remote_cartridges_types = _remoteDB.CARTRIDGE_TYPES.Where(x => guids.Contains(x.GUID)).ToList();

            OnProgress(LogManager.Log("Querying all remote jobs..."));
            var remote_jobs = remote_machines.SelectMany(x => x.JOBS).ToList();

            OnProgress(LogManager.Log("Querying all remote job runs..."));
            var remote_jobs_runs = remote_jobs.SelectMany(x => x.JOB_RUNS).ToList();

            OnProgress(LogManager.Log("Querying all remote segments..."));
            var remote_segments = remote_jobs.SelectMany(x => x.SEGMENTS).ToList();

            OnProgress(LogManager.Log("Querying all remote brush stops..."));
            var remote_brush_stops = remote_segments.SelectMany(x => x.BRUSH_STOPS).ToList();

            //LogManager.Log("Querying all remote CATS...");
            //guids = remote_machines.Select(x => x.GUID).ToList();
            //var remote_cats = _remoteDB.CATS.Where(x => guids.Contains(x.MACHINE_GUID)).ToList();


            foreach (var config in _remoteDB.SYNC_CONFIGURATIONS.Where(x => (SyncConfiguration)x.SYNC_TYPE == SyncConfiguration.OverwriteLocal))
            {
                OverrideTable(config);
            }


            OnProgress(LogManager.Log("Comparing addresses"));
            CompareCollections(remote_addresses, local_addresses, _remoteDB.ADDRESSES, _localDB.ADDRESSES);

            OnProgress(LogManager.Log("Comparing configurations"));
            CompareCollections(remote_configurations, local_configurations, _remoteDB.CONFIGURATIONS, _localDB.CONFIGURATIONS);

            OnProgress(LogManager.Log("Comparing configurations dispensers"));
            CompareCollections(remote_ids_packs, local_ids_packs, _remoteDB.IDS_PACKS, _localDB.IDS_PACKS);

            OnProgress(LogManager.Log("Comparing contacts"));
            CompareCollections(remote_contacts, local_contacts, _remoteDB.CONTACTS, _localDB.CONTACTS);

            OnProgress(LogManager.Log("Comparing machines"));
            CompareCollections(remote_machines, local_machines, _remoteDB.MACHINES, _localDB.MACHINES);

            OnProgress(LogManager.Log("Comparing machines configuration"));
            CompareCollections(remote_machines_configurations, local_machines_configurations, _remoteDB.MACHINES_CONFIGURATIONS, _localDB.MACHINES_CONFIGURATIONS);

            OnProgress(LogManager.Log("Comparing machines events"));
            CompareCollections(remote_machines_events, local_machines_events, _remoteDB.MACHINES_EVENTS, _localDB.MACHINES_EVENTS);

            OnProgress(LogManager.Log("Comparing organizations"));
            CompareCollections(remote_organizations, local_organizations, _remoteDB.ORGANIZATIONS, _localDB.ORGANIZATIONS);

            OnProgress(LogManager.Log("Comparing users"));
            CompareCollections(remote_users, local_users, _remoteDB.USERS, _localDB.USERS);

            OnProgress(LogManager.Log("Comparing users roles"));
            CompareCollections(remote_users_roles, local_users_roles, _remoteDB.USERS_ROLES, _localDB.USERS_ROLES);

            OnProgress(LogManager.Log("Comparing jobs"));
            CompareCollections(remote_jobs, local_jobs, _remoteDB.JOBS, _localDB.JOBS);

            OnProgress(LogManager.Log("Comparing job runs"));
            CompareCollections(remote_jobs_runs, local_job_runs, _remoteDB.JOB_RUNS, _localDB.JOB_RUNS);

            OnProgress(LogManager.Log("Comparing segments"));
            CompareCollections(remote_segments, local_segments, _remoteDB.SEGMENTS, _localDB.SEGMENTS);

            OnProgress(LogManager.Log("Comparing brush stops"));
            CompareCollections(remote_brush_stops, local_brush_stops, _remoteDB.BRUSH_STOPS, _localDB.BRUSH_STOPS);

            //LogManager.Log("Comparing cats");
            //CompareCollections(remote_cats, local_cats, _remoteDB.CATS, _localDB.CATS);

            OnProgress(LogManager.Log("Comparison done!"));

            return _diffs;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            _remoteDB.Dispose();
            _localDB.Dispose();
        }

        /// <summary>
        /// Copies source entity properties to the destination entity.
        /// </summary>
        /// <param name="source">The source entity.</param>
        /// <param name="destination">The destination entity.</param>
        /// <exception cref="InvalidOperationException"></exception>
        private void CopyEntity(Object source, Object destination)
        {
            foreach (var prop in source.GetType().GetProperties().Where(x => x.PropertyType.IsPrimitive || x.PropertyType == typeof(String) || x.PropertyType == typeof(DateTime) || x.PropertyType == typeof(byte[])))
            {
                if (destination.GetType().GetProperty(prop.Name) == null)
                {
                    throw LogManager.Log(new InvalidOperationException(String.Format("Property '{0}' not found on destination table '{1}'.", prop.Name, destination.GetType().Name)));
                }

                if (prop.PropertyType == typeof(Int64))
                {
                    destination.GetType().GetProperty(prop.Name).SetValue(destination, Convert.ToInt32((Int64)prop.GetValue(source)));
                }
                else
                {
                    destination.GetType().GetProperty(prop.Name).SetValue(destination, prop.GetValue(source));
                }
            }
        }

        /// <summary>
        /// Compares a collection of source entities to a collection of destination entities.
        /// </summary>
        /// <typeparam name="Master">The type of the aster.</typeparam>
        /// <typeparam name="Slave">The type of the lave.</typeparam>
        /// <param name="masterCollection">The master collection.</param>
        /// <param name="slaveCollection">The slave collection.</param>
        /// <param name="masterSet">The master set.</param>
        /// <param name="slaveSet">The slave set.</param>
        private void CompareCollections<Master, Slave>(List<Master> masterCollection, List<Slave> slaveCollection, DbSet<Master> masterSet, DbSet<Slave> slaveSet) where Master : class where Slave : class
        {
            var slaveProp = typeof(Slave).GetProperty("GUID");
            var masterProp = typeof(Master).GetProperty("GUID");

            List<Slave> compared = new List<Slave>();

            foreach (var masterRow in masterCollection)
            {
                Slave slaveRow = slaveCollection.SingleOrDefault(x => slaveProp.GetValue(x).ToString() == masterProp.GetValue(masterRow).ToString());
                CompareEntities(masterRow, slaveRow, masterSet, slaveSet);

                if (slaveRow != null)
                {
                    compared.Add(slaveRow);
                }
            }

            foreach (var slaveRow in slaveCollection.Where(x => !compared.Contains(x)))
            {
                CompareEntities(masterCollection.SingleOrDefault(x => masterProp.GetValue(x).ToString() == slaveProp.GetValue(slaveRow).ToString()), slaveRow, masterSet, slaveSet);
            }
        }

        /// <summary>
        /// Overrides the table by the specified synchronization configuration.
        /// </summary>
        /// <param name="config">The configuration.</param>
        private void OverrideTable(remote.SYNC_CONFIGURATIONS config)
        {
            OnProgress(LogManager.Log("Generating table override difference for " + config.TABLE_NAME + "..."));

            var master = _remoteDB.GetType().GetProperty(config.TABLE_NAME).GetValue(_remoteDB) as IEnumerable;
            var slave = _localDB.GetType().GetProperty(config.TABLE_NAME).GetValue(_localDB) as IEnumerable;

            _diffs.Add(new Diff(DiffAction.ReplaceTableDataInSlave, "Override all rows on slave table " + config.TABLE_NAME, () =>
            {
                OnProgress(LogManager.Log("Overwriting slave table " + config.TABLE_NAME + "..."));

                _localDB.Database.ExecuteSqlCommand("DELETE FROM " + config.TABLE_NAME + ";");

                foreach (var entity in master)
                {
                    var newRow = slave.GetType().GetMethods().Where(x => x.Name == "Create").First().Invoke(slave, new object[] { });
                    CopyEntity(entity, newRow);
                    slave.GetType().GetMethod("Add").Invoke(slave, new object[] { newRow });
                }

            }, null));
        }

        /// <summary>
        /// Compares the specified entities.
        /// </summary>
        /// <typeparam name="Master">The type of the master.</typeparam>
        /// <typeparam name="Slave">The type of the slave.</typeparam>
        /// <param name="master">The master.</param>
        /// <param name="slave">The slave.</param>
        private void CompareEntities<Master, Slave>(Master master, Slave slave) where Master : class where Slave : class
        {
            Diff diff = null;

            DateTime masterDate = (DateTime)master.GetType().GetProperty("LAST_UPDATED").GetValue(master);
            DateTime slaveDate = (DateTime)slave.GetType().GetProperty("LAST_UPDATED").GetValue(slave);

            if (masterDate > slaveDate)
            {
                diff = new Diff(DiffAction.UpdateRowInSlave, "Update row in slave table " + typeof(Master).Name, () =>
                {
                    OnProgress(LogManager.Log("Updating row in slave table " + typeof(Master).Name));
                    CopyEntity(master, slave);
                }, null);
            }
            else if (slaveDate > masterDate)
            {
                diff = new Diff(DiffAction.UpdateRowInMaster, "Update row in master table " + typeof(Master).Name, () =>
                {
                    OnProgress(LogManager.Log("Updating row in master table " + typeof(Master).Name));
                    CopyEntity(slave, master);
                }, null);
            }

            if (diff != null)
            {
                _diffs.Add(diff);
            }
        }

        /// <summary>
        /// Compares the entities.
        /// </summary>
        /// <typeparam name="Master">The type of the master.</typeparam>
        /// <typeparam name="Slave">The type of the slave.</typeparam>
        /// <param name="master">The master.</param>
        /// <param name="slave">The slave.</param>
        /// <param name="masterSet">The master set.</param>
        /// <param name="slaveSet">The slave set.</param>
        private void CompareEntities<Master, Slave>(Master master, Slave slave, DbSet<Master> masterSet, DbSet<Slave> slaveSet) where Master : class where Slave : class
        {
            bool skipAddToSlave = false;

            if (slave == null && _remoteDB.SYNC_CONFIGURATIONS.ToList().Exists(x => (SyncConfiguration)x.SYNC_TYPE == SyncConfiguration.SynchronizeToRemote && x.TABLE_NAME.SingularizeMVC() == typeof(Master).Name))
            {
                skipAddToSlave = true;
            }

            if (slave == null && !skipAddToSlave)
            {
                _diffs.Add(new Diff(DiffAction.AddRowToSlave, "Add row to slave table " + typeof(Master).Name, () =>
               {
                   OnProgress(LogManager.Log("Adding row to slave table " + typeof(Master).Name));
                   Slave newRow = slaveSet.Create();
                   CopyEntity(master, newRow);
                   slaveSet.Add(newRow);

               }, null));

                return;
            }

            if (master == null)
            {
                _diffs.Add(new Diff(DiffAction.AddRowToMaster, "Add row to master table " + typeof(Master).Name, () =>
                {
                    OnProgress(LogManager.Log("Adding row to master table " + typeof(Master).Name));
                    Master newRow = masterSet.Create();
                    CopyEntity(slave, newRow);
                    masterSet.Add(newRow);

                }, null));

                return;
            }

            if (slave != null && master != null)
            {
                CompareEntities(master, slave);
            }
        }

        /// <summary>
        /// Raises the <see cref="Progress"/> event.
        /// </summary>
        /// <returns></returns>
        protected virtual void OnProgress(String message)
        {
            Progress?.Invoke(this, message);
        }
    }
}