Exemple #1
0
        protected override void Process(System.Web.HttpContext context, ResourceManager resourceManager)
        {
            context.Response.ContentType = ContentTypes.Text.Plain;
            context.Response.StatusCode  = 200;

            string?sectorName = GetStringOption(context, "sector");
            string?type       = GetStringOption(context, "type");
            string?regex      = GetStringOption(context, "regex");
            string?milieu     = GetStringOption(context, "milieu");

            // NOTE: This (re)initializes a static data structure used for
            // resolving names into sector locations, so needs to be run
            // before any other objects (e.g. Worlds) are loaded.
            SectorMap.Flush();
            SectorMap map = SectorMap.GetInstance(resourceManager);

            var sectorQuery = from sector in map.Sectors
                              where (sectorName == null || sector.Names[0].Text.StartsWith(sectorName, ignoreCase: true, culture: CultureInfo.InvariantCulture)) &&
                              (sector.DataFile != null) &&
                              (type == null || sector.DataFile.Type == type) &&
                              (!sector.Tags.Contains("ZCR")) &&
                              (!sector.Tags.Contains("meta")) &&
                              (milieu == null || sector.CanonicalMilieu == milieu)
                              orderby sector.Names[0].Text
                              select sector;

            Dictionary <string, HashSet <string> > codes = new Dictionary <string, HashSet <string> >();

            Regex filter = new Regex(regex ?? ".*");

            foreach (var sector in sectorQuery)
            {
                WorldCollection?worlds = sector.GetWorlds(resourceManager, cacheResults: false);
                if (worlds == null)
                {
                    continue;
                }

                foreach (var code in worlds
                         .SelectMany(world => world.Codes)
                         .Where(code => filter.IsMatch(code) && !s_knownCodes.IsMatch(code)))
                {
                    if (!codes.ContainsKey(code))
                    {
                        codes.Add(code, new HashSet <string>());
                    }
                    codes[code].Add($"{sector.Names[0].Text} [{sector.CanonicalMilieu}]");
                }
            }

            foreach (var code in codes.Keys.OrderBy(s => s))
            {
                context.Response.Output.Write(code + " - ");
                foreach (var sector in codes[code].OrderBy(s => s))
                {
                    context.Response.Output.Write(sector + " ");
                }
                context.Response.Output.WriteLine("");
            }
        }
Exemple #2
0
            public override void Process(ResourceManager resourceManager)
            {
                // NOTE: This (re)initializes a static data structure used for
                // resolving names into sector locations, so needs to be run
                // before any other objects (e.g. Worlds) are loaded.
                SectorMap map = SectorMap.GetInstance(resourceManager);

                SendResult(map.GetMilieux().Select(code => new Results.Milieu(code)).ToList());
            }
            public override void Process()
            {
                ResourceManager resourceManager = new ResourceManager(context.Server);

                // NOTE: This (re)initializes a static data structure used for
                // resolving names into sector locations, so needs to be run
                // before any other objects (e.g. Worlds) are loaded.
                SectorMap map = SectorMap.GetInstance(resourceManager);

                // Filter parameters
                string milieu      = GetStringOption("milieu") ?? GetStringOption("era");
                bool   requireData = GetBoolOption("requireData", defaultValue: false);

                string[] tags = GetStringsOption("tag");

                UniverseResult data = new UniverseResult();

                foreach (Sector sector in map.Sectors)
                {
                    if (requireData && sector.DataFile == null)
                    {
                        continue;
                    }

                    if (sector.Tags.Contains("meta"))
                    {
                        continue;
                    }

                    if (milieu != null && sector.DataFile?.Milieu != milieu)
                    {
                        continue;
                    }

                    if (tags != null && !tags.Any(tag => sector.Tags.Contains(tag)))
                    {
                        continue;
                    }

                    data.Sectors.Add(new UniverseResult.SectorResult(sector));
                }

                SendResult(context, data);
            }
Exemple #4
0
        protected override void Process(System.Web.HttpContext context)
        {
            ResourceManager resourceManager = new ResourceManager(context.Server);

            // NOTE: This (re)initializes a static data structure used for
            // resolving names into sector locations, so needs to be run
            // before any other objects (e.g. Worlds) are loaded.
            SectorMap map = SectorMap.GetInstance(resourceManager);

            context.Response.ContentType  = MediaTypeNames.Text.Plain;
            context.Response.BufferOutput = false;

            foreach (Sector sector in map.Sectors)
            {
                WorldCollection worlds = sector.GetWorlds(resourceManager);
                if (worlds == null)
                {
                    continue;
                }
                foreach (World world in worlds)
                {
                    List <string> list = new List <string> {
                        sector.Names[0].Text,
                        sector.X.ToString(),
                        sector.Y.ToString(),
                        world.X.ToString(),
                        world.Y.ToString(),
                        world.Name,
                        world.UWP,
                        world.Bases,
                        world.Remarks,
                        world.PBG,
                        world.Allegiance,
                        world.Stellar
                    };
                    WriteCSV(context.Response.Output, list);
                }
            }
        }
        protected override void Process(System.Web.HttpContext context)
        {
            context.Response.ContentType  = MediaTypeNames.Text.Plain;
            context.Response.BufferOutput = false;

            ResourceManager resourceManager = new ResourceManager(context.Server);

            string sectorName = GetStringOption(context, "sector");
            string type       = GetStringOption(context, "type");
            string milieu     = GetStringOption(context, "milieu");
            string tag        = GetStringOption(context, "tag");

            // NOTE: This (re)initializes a static data structure used for
            // resolving names into sector locations, so needs to be run
            // before any other objects (e.g. Worlds) are loaded.
            SectorMap.Flush();
            SectorMap map = SectorMap.GetInstance(resourceManager);

            var sectorQuery = from sector in map.Sectors
                              where (sectorName == null || sector.Names[0].Text.StartsWith(sectorName, ignoreCase: true, culture: CultureInfo.InvariantCulture)) &&
                              (sector.DataFile != null) &&
                              (type == null || sector.DataFile.Type == type) &&
                              (milieu == null || sector.CanonicalMilieu == milieu) &&
                              (tag == null || sector.Tags.Contains(tag)) &&
                              (sector.Tags.Contains("OTU") || sector.Tags.Contains("Apocryphal") || sector.Tags.Contains("Faraway"))
                              orderby sector.Names[0].Text
                              select sector;

            foreach (var sector in sectorQuery)
            {
                context.Response.Output.WriteLine(sector.Names[0].Text);
#if DEBUG
                WorldCollection worlds = sector.GetWorlds(resourceManager, cacheResults: false);

                if (worlds != null)
                {
                    double pop = worlds.Select(w => w.Population).Sum();
                    if (pop > 0)
                    {
                        context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: {pop / 1e9:#,###.##} billion");
                    }
                    else
                    {
                        context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: N/A");
                    }
                    worlds.ErrorList.Report(context.Response.Output);
                }
                else
                {
                    context.Response.Output.WriteLine("0 world(s)");
                }

                foreach (IAllegiance item in sector.Borders.AsEnumerable <IAllegiance>()
                         .Concat(sector.Routes.AsEnumerable <IAllegiance>())
                         .Concat(sector.Labels.AsEnumerable <IAllegiance>()))
                {
                    if (string.IsNullOrWhiteSpace(item.Allegiance))
                    {
                        continue;
                    }
                    if (sector.GetAllegianceFromCode(item.Allegiance) == null)
                    {
                        context.Response.Output.WriteLine($"Undefined allegiance code: {item.Allegiance} (on {item.GetType().Name})");
                    }
                }

                foreach (var route in sector.Routes)
                {
                    System.Drawing.Point startSector = sector.Location, endSector = sector.Location;
                    startSector.Offset(route.StartOffset);
                    endSector.Offset(route.EndOffset);

                    Location startLocation = new Location(startSector, route.Start);
                    Location endLocation   = new Location(endSector, route.End);
                    int      distance = Astrometrics.HexDistance(Astrometrics.LocationToCoordinates(startLocation),
                                                                 Astrometrics.LocationToCoordinates(endLocation));
                    if (distance == 0)
                    {
                        context.Response.Output.WriteLine($"Error: Route length {distance}: {route.ToString()}");
                    }
                    else if (distance > 4)
                    {
                        context.Response.Output.WriteLine($"Warning: Route length {distance}: {route.ToString()}");
                    }

                    /*
                     * This fails because of routes that use e.g. 3341-style coordinates
                     * It will also be extremely slow due to loading world lists w/o caching
                     *                  {
                     *                      var w = map.FromLocation(startSector).GetWorlds(resourceManager, cacheResults: false);
                     *                      if (w != null)
                     *                      {
                     *                          if (w[route.StartPoint.X, route.StartPoint.Y] == null)
                     *                              context.Response.Output.WriteLine($"Route start empty hex: {route.ToString()}");
                     *                      }
                     *                  }
                     *                  {
                     *                      var w = map.FromLocation(endSector).GetWorlds(resourceManager, cacheResults: false);
                     *                      if (w != null)
                     *                      {
                     *                          if (w[route.EndPoint.X, route.EndPoint.Y] == null)
                     *                              context.Response.Output.WriteLine($"Route end empty hex: {route.ToString()}");
                     *                      }
                     *                  }
                     */
                }
#endif
                context.Response.Output.WriteLine();
            }
            return;
        }
        protected override void Process(System.Web.HttpContext context, ResourceManager resourceManager)
        {
            context.Response.ContentType  = ContentTypes.Text.Plain;
            context.Response.BufferOutput = false;

            string sectorName   = GetStringOption(context, "sector");
            string type         = GetStringOption(context, "type");
            string milieu       = GetStringOption(context, "milieu");
            string tag          = GetStringOption(context, "tag");
            bool   hide_tl      = GetBoolOption(context, "hide-tl");
            bool   hide_gov     = GetBoolOption(context, "hide-gov");
            bool   hide_stellar = GetBoolOption(context, "hide-stellar");

            ErrorLogger.Severity severity = GetBoolOption(context, "warnings", true) ? 0 : ErrorLogger.Severity.Error;

            // NOTE: This (re)initializes a static data structure used for
            // resolving names into sector locations, so needs to be run
            // before any other objects (e.g. Worlds) are loaded.
            SectorMap.Flush();
            SectorMap map = SectorMap.GetInstance(resourceManager);

            var sectorQuery = from sector in map.Sectors
                              where (sectorName == null || sector.Names[0].Text.StartsWith(sectorName, ignoreCase: true, culture: CultureInfo.InvariantCulture)) &&
                              (sector.DataFile != null) &&
                              (type == null || sector.DataFile.Type == type) &&
                              (milieu == null || sector.CanonicalMilieu == milieu) &&
                              (tag == null || sector.Tags.Contains(tag)) &&
                              (sector.Tags.Contains("OTU") || sector.Tags.Contains("Apocryphal") || sector.Tags.Contains("Faraway"))
                              orderby sector.Names[0].Text
                              select sector;

            foreach (var sector in sectorQuery)
            {
                context.Response.Output.WriteLine($"{sector.Names[0].Text} - {sector.Milieu}");
#if DEBUG
                int error_count   = 0;
                int warning_count = 0;

                try
                {
                    WorldCollection worlds = sector.GetWorlds(resourceManager, cacheResults: false);
                    if (worlds != null)
                    {
                        double pop = worlds.Select(w => w.Population).Sum();
                        if (pop > 0)
                        {
                            context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: {pop / 1e9:#,###.##} billion");
                        }
                        else
                        {
                            context.Response.Output.WriteLine($"{worlds.Count()} world(s) - population: N/A");
                        }
                        worlds.ErrorList.Report(context.Response.Output, severity, (ErrorLogger.Record record) =>
                        {
                            if (hide_gov && (record.message.StartsWith("UWP: Gov") || record.message.StartsWith("Gov")))
                            {
                                return(false);
                            }
                            if (hide_tl && record.message.StartsWith("UWP: TL"))
                            {
                                return(false);
                            }
                            if (hide_stellar && record.message.StartsWith("Invalid stellar data:"))
                            {
                                return(false);
                            }
                            return(true);
                        });
                        error_count   += worlds.ErrorList.CountOf(ErrorLogger.Severity.Error);
                        warning_count += worlds.ErrorList.CountOf(ErrorLogger.Severity.Warning);
                    }
                    else
                    {
                        context.Response.Output.WriteLine("0 world(s)");
                    }
                }
                catch (ParseException ex)
                {
                    context.Response.Output.WriteLine($"Bad data file: {ex.Message}");
                }

                foreach (IAllegiance item in sector.Borders.AsEnumerable <IAllegiance>()
                         .Concat(sector.Routes.AsEnumerable <IAllegiance>())
                         .Concat(sector.Labels.AsEnumerable <IAllegiance>()))
                {
                    if (string.IsNullOrWhiteSpace(item.Allegiance))
                    {
                        continue;
                    }
                    if (sector.GetAllegianceFromCode(item.Allegiance) == null)
                    {
                        context.Response.Output.WriteLine($"Undefined allegiance code: {item.Allegiance} (on {item.GetType().Name})");
                    }
                }

                foreach (var route in sector.Routes)
                {
                    System.Drawing.Point startSector = sector.Location, endSector = sector.Location;
                    startSector.Offset(route.StartOffset);
                    endSector.Offset(route.EndOffset);

                    int distance = Astrometrics.HexDistance(
                        Astrometrics.LocationToCoordinates(new Location(startSector, route.Start)),
                        Astrometrics.LocationToCoordinates(new Location(endSector, route.End)));
                    if (distance == 0)
                    {
                        context.Response.Output.WriteLine($"Error: Route length {distance}: {route.ToString()}");
                        ++error_count;
                    }
                    else if (distance > 4)
                    {
                        if (severity <= ErrorLogger.Severity.Warning)
                        {
                            context.Response.Output.WriteLine($"Warning: Route length {distance}: {route.ToString()}");
                        }
                        ++warning_count;
                    }

                    /*
                     * This fails because of routes that use e.g. 3341-style coordinates
                     * It will also be extremely slow due to loading world lists w/o caching
                     *                  {
                     *                      var w = map.FromLocation(startSector).GetWorlds(resourceManager, cacheResults: false);
                     *                      if (w != null)
                     *                      {
                     *                          if (w[route.StartPoint.X, route.StartPoint.Y] == null)
                     *                              context.Response.Output.WriteLine($"Route start empty hex: {route.ToString()}");
                     *                      }
                     *                  }
                     *                  {
                     *                      var w = map.FromLocation(endSector).GetWorlds(resourceManager, cacheResults: false);
                     *                      if (w != null)
                     *                      {
                     *                          if (w[route.EndPoint.X, route.EndPoint.Y] == null)
                     *                              context.Response.Output.WriteLine($"Route end empty hex: {route.ToString()}");
                     *                      }
                     *                  }
                     */
                }
                context.Response.Output.WriteLine($"{error_count} errors, {warning_count} warnings.");
#endif
                context.Response.Output.WriteLine();
            }
            return;
        }
        public static void PopulateDatabase(ResourceManager resourceManager, Action <string> statusCallback)
        {
            // Lock to prevent indexing twice, without blocking tile requests.
            lock (SearchEngine.s_lock)
            {
                // NOTE: This (re)initializes a static data structure used for
                // resolving names into sector locations, so needs to be run
                // before any other objects (e.g. Worlds) are loaded.
                SectorMap map = SectorMap.GetInstance(resourceManager);

                using (var connection = DBUtil.MakeConnection())
                {
                    SqlCommand sqlCommand;

                    //
                    // Repopulate the tables - locally first
                    // FUTURE: will need to batch this up rather than keep it in memory!
                    //

                    DataTable dt_sectors = new DataTable();
                    for (int i = 0; i < SECTORS_COLUMNS.Length; ++i)
                    {
                        dt_sectors.Columns.Add(new DataColumn());
                    }

                    DataTable dt_subsectors = new DataTable();
                    for (int i = 0; i < SUBSECTORS_COLUMNS.Length; ++i)
                    {
                        dt_subsectors.Columns.Add(new DataColumn());
                    }

                    DataTable dt_worlds = new DataTable();
                    for (int i = 0; i < WORLDS_COLUMNS.Length; ++i)
                    {
                        dt_worlds.Columns.Add(new DataColumn());
                    }

                    DataTable dt_labels = new DataTable();
                    for (int i = 0; i < LABELS_COLUMNS.Length; ++i)
                    {
                        dt_labels.Columns.Add(new DataColumn());
                    }

                    // Map of (milieu, string) => [ points ... ]
                    Dictionary <Tuple <string, string>, List <Point> > labels = new Dictionary <Tuple <string, string>, List <Point> >();
                    Action <string, string, Point> AddLabel = (string milieu, string text, Point coords) => {
                        if (text == null)
                        {
                            return;
                        }
                        text = SanifyLabel(text);
                        var key = Tuple.Create(milieu, text);
                        if (!labels.ContainsKey(key))
                        {
                            labels.Add(key, new List <Point>());
                        }
                        labels[key].Add(coords);
                    };

                    statusCallback("Parsing data...");
                    foreach (Sector sector in map.Sectors)
                    {
                        // TODO: Index alternate milieu
                        if (!sector.Tags.Contains("OTU") && !sector.Tags.Contains("Faraway"))
                        {
                            continue;
                        }

                        foreach (Name name in sector.Names)
                        {
                            DataRow row = dt_sectors.NewRow();
                            row.ItemArray = new object[] { sector.CanonicalMilieu, sector.X, sector.Y, name.Text };
                            dt_sectors.Rows.Add(row);
                        }
                        if (!string.IsNullOrEmpty(sector.Abbreviation))
                        {
                            DataRow row = dt_sectors.NewRow();
                            row.ItemArray = new object[] { sector.CanonicalMilieu, sector.X, sector.Y, sector.Abbreviation };
                            dt_sectors.Rows.Add(row);
                        }

                        foreach (Subsector subsector in sector.Subsectors)
                        {
                            DataRow row = dt_subsectors.NewRow();
                            row.ItemArray = new object[] { sector.CanonicalMilieu, sector.X, sector.Y, subsector.Index, subsector.Name };
                            dt_subsectors.Rows.Add(row);
                        }

                        foreach (Border border in sector.Borders.Where(b => b.ShowLabel))
                        {
                            AddLabel(
                                sector.CanonicalMilieu,
                                border.GetLabel(sector),
                                Astrometrics.LocationToCoordinates(new Location(sector.Location, border.LabelPosition)));
                        }

                        foreach (Label label in sector.Labels)
                        {
                            AddLabel(
                                sector.CanonicalMilieu,
                                label.Text,
                                Astrometrics.LocationToCoordinates(new Location(sector.Location, label.Hex)));
                        }

#if DEBUG
                        if (!sector.Selected)
                        {
                            continue;
                        }
#endif
                        // NOTE: May need to page this at some point
                        WorldCollection worlds = sector.GetWorlds(resourceManager, cacheResults: false);
                        if (worlds == null)
                        {
                            continue;
                        }

                        var world_query = from world in worlds
                                          where !world.IsPlaceholder
                                          select world;
                        foreach (World world in world_query)
                        {
                            DataRow row = dt_worlds.NewRow();
                            row.ItemArray = new object[] {
                                sector.CanonicalMilieu,
                                world.Coordinates.X,
                                world.Coordinates.Y,
                                sector.X,
                                sector.Y,
                                world.X,
                                world.Y,
                                string.IsNullOrEmpty(world.Name) ? (object)DBNull.Value : (object)world.Name,
                                world.UWP,
                                world.Remarks,
                                world.PBG,
                                string.IsNullOrEmpty(world.Zone) ? "G" : world.Zone,
                                world.Allegiance,
                                sector.Names.Count > 0 ? (object)sector.Names[0] : (object)DBNull.Value
                            };

                            dt_worlds.Rows.Add(row);
                        }
                    }

                    foreach (KeyValuePair <Tuple <string, string>, List <Point> > entry in labels)
                    {
                        string       milieu = entry.Key.Item1;
                        string       name   = entry.Key.Item2;
                        List <Point> points = entry.Value;

                        Point avg = new Point(
                            (int)Math.Round(points.Select(p => p.X).Average()),
                            (int)Math.Round(points.Select(p => p.Y).Average()));
                        Point min    = new Point(points.Select(p => p.X).Min(), points.Select(p => p.Y).Min());
                        Point max    = new Point(points.Select(p => p.X).Max(), points.Select(p => p.Y).Max());
                        Size  size   = new Size(max.X - min.X, max.Y - min.Y);
                        int   radius = Math.Max(size.Width, size.Height);

                        DataRow row = dt_labels.NewRow();
                        row.ItemArray = new object[] {
                            milieu,
                            avg.X,
                            avg.Y,
                            radius,
                            name
                        };
                        dt_labels.Rows.Add(row);
                    }

                    //
                    // Rebuild the tables with fresh schema
                    //

                    const string INDEX_OPTIONS = " WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)";

                    const string DROP_TABLE_IF_EXISTS = "IF EXISTS(SELECT 1 FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(N'{0}') AND type = (N'U')) DROP TABLE {0}";

                    string[] rebuild_schema =
                    {
                        string.Format(DROP_TABLE_IF_EXISTS,                                                          "sectors"),
                        "CREATE TABLE sectors (" + string.Join(",",                                                  SECTORS_COLUMNS) + ")",
                        "CREATE NONCLUSTERED INDEX sector_name ON sectors ( name ASC )" + INDEX_OPTIONS,
                        "CREATE NONCLUSTERED INDEX sector_milieu ON sectors ( milieu ASC )" + INDEX_OPTIONS,

                        string.Format(DROP_TABLE_IF_EXISTS,                                                          "subsectors"),
                        "CREATE TABLE subsectors (" + string.Join(",",                                               SUBSECTORS_COLUMNS) + ")",
                        "CREATE NONCLUSTERED INDEX subsector_name ON subsectors ( name ASC )" + INDEX_OPTIONS,
                        "CREATE NONCLUSTERED INDEX subsector_milieu ON subsectors ( milieu ASC )" + INDEX_OPTIONS,

                        string.Format(DROP_TABLE_IF_EXISTS,                                                          "worlds"),
                        "CREATE TABLE worlds (" + string.Join(",",                                                   WORLDS_COLUMNS) + ")",
                        "CREATE NONCLUSTERED INDEX world_name ON worlds ( name ASC )" + INDEX_OPTIONS,
                        "CREATE NONCLUSTERED INDEX world_uwp ON worlds ( uwp ASC )" + INDEX_OPTIONS,
                        "CREATE NONCLUSTERED INDEX world_pbg ON worlds ( pbg ASC )" + INDEX_OPTIONS,
                        "CREATE NONCLUSTERED INDEX world_alleg ON worlds ( alleg ASC )" + INDEX_OPTIONS,
                        "CREATE NONCLUSTERED INDEX world_sector_name ON worlds ( sector_name ASC )" + INDEX_OPTIONS,
                        "CREATE NONCLUSTERED INDEX world_milieu ON worlds ( milieu ASC )" + INDEX_OPTIONS,

                        string.Format(DROP_TABLE_IF_EXISTS,                                                          "labels"),
                        "CREATE TABLE labels (" + string.Join(",",                                                   LABELS_COLUMNS) + ")",
                        "CREATE NONCLUSTERED INDEX name ON labels ( name ASC )" + INDEX_OPTIONS,
                        "CREATE NONCLUSTERED INDEX milieu ON labels ( milieu ASC )" + INDEX_OPTIONS,
                    };

                    statusCallback("Rebuilding schema...");
                    foreach (string cmd in rebuild_schema)
                    {
                        sqlCommand = new SqlCommand(cmd, connection);
                        sqlCommand.ExecuteNonQuery();
                    }

                    //
                    // And shovel the data into the database en masse
                    //
                    Action <string, DataTable, int> BulkInsert = (string name, DataTable table, int batchSize) => {
                        using (var bulk = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, null))
                        {
                            statusCallback($"Writing {table.Rows.Count} {name}...");
                            bulk.BatchSize            = batchSize;
                            bulk.DestinationTableName = name;
                            bulk.WriteToServer(table);
                        }
                    };

                    BulkInsert("sectors", dt_sectors, dt_sectors.Rows.Count);
                    BulkInsert("subsectors", dt_subsectors, dt_subsectors.Rows.Count);
                    BulkInsert("worlds", dt_worlds, 4096);
                    BulkInsert("labels", dt_labels, dt_labels.Rows.Count);
                }
                statusCallback("Complete!");
            }
        }