예제 #1
0
        /// <param name="pulseProgress">делегат вызываемый для отображения прогресс, первый параметр текущее значение. Второй сколько всего.</param>
        public List <PointLatLng> GetGeometryOfRoute(long[] route, Action <uint, uint> pulseProgress)
        {
            //Запрашиваем кешь одним запросом для всего маршрута.
            LoadDBCacheIfNeed(GenerateWaysOfRoute(route));

            List <PointLatLng> resultRoute = new List <PointLatLng>();

            for (int ix = 1; ix < route.Length; ix++)
            {
                CachedDistance way = GetCachedGeometry(route[ix - 1], route[ix], false);

                if (way?.PolylineGeometry != null)
                {
                    var decodedPoints = Polyline.DecodePolyline(way.PolylineGeometry);
                    resultRoute.AddRange(decodedPoints.Select(p => new PointLatLng(p.Latitude, p.Longitude)));
                }
                else
                {
                    double lat, lon;
                    CachedDistance.GetLatLon(route[ix - 1], out lat, out lon);
                    resultRoute.Add(new PointLatLng(lat, lon));
                    CachedDistance.GetLatLon(route[ix], out lat, out lon);
                    resultRoute.Add(new PointLatLng(lat, lon));
                }

                if (pulseProgress != null)
                {
                    pulseProgress((uint)ix, (uint)route.Length);
                }
            }

            return(resultRoute);
        }
예제 #2
0
        /// <summary>
        /// Время пути в секундах от базы до точки
        /// </summary>
        public int TimeFromBaseSec(GeographicGroup fromBase, DeliveryPoint toDP)
        {
            var toHash       = CachedDistance.GetHash(toDP);
            var fromBaseHash = CachedDistance.GetHash(fromBase);

            return(TimeSec(fromBaseHash, toHash));
        }
예제 #3
0
 private int GetSimpleDistance(WayHash way)
 {
     return((int)(GMap.NET.MapProviders.GMapProviders.EmptyProvider.Projection.GetDistance(
                      CachedDistance.GetPointLatLng(way.FromHash),
                      CachedDistance.GetPointLatLng(way.ToHash)
                      ) * 1000));
 }
예제 #4
0
        /// <summary>
        /// Расстояние в метрах от точки до базы.
        /// </summary>
        public int DistanceToBaseMeter(DeliveryPoint fromDP, GeographicGroup toBase)
        {
            var fromHash   = CachedDistance.GetHash(fromDP);
            var toBaseHash = CachedDistance.GetHash(toBase);

            return(DistanceMeter(fromHash, toBaseHash));
        }
예제 #5
0
        /// <summary>
        /// Всемя пути в секундах между точками
        /// </summary>
        public int TimeSec(DeliveryPoint fromDP, DeliveryPoint toDP)
        {
            var fromHash = CachedDistance.GetHash(fromDP);
            var toHash   = CachedDistance.GetHash(toDP);

            return(TimeSec(fromHash, toHash));
        }
예제 #6
0
        /// <summary>
        /// Почучаем расстояния в метрах между точками
        /// </summary>
        public int DistanceMeter(DeliveryPoint fromDP, DeliveryPoint toDP)
        {
            var fromHash = CachedDistance.GetHash(fromDP);
            var toHash   = CachedDistance.GetHash(toDP);

            return(DistanceMeter(fromHash, toHash));
        }
예제 #7
0
        /// <summary>
        /// Расстояние в метрах от базы до точки.
        /// </summary>
        public int DistanceFromBaseMeter(GeographicGroup fromBase, DeliveryPoint toDP)
        {
            var toHash       = CachedDistance.GetHash(toDP);
            var fromBaseHash = CachedDistance.GetHash(fromBase);

            return(DistanceMeter(fromBaseHash, toHash));
        }
예제 #8
0
        /// <summary>
        /// Время пути в секундах от точки до базы.
        /// </summary>
        public int TimeToBaseSec(DeliveryPoint fromDP, GeographicGroup toBase)
        {
            var fromHash   = CachedDistance.GetHash(fromDP);
            var toBaseHash = CachedDistance.GetHash(toBase);

            return(TimeSec(fromHash, toBaseHash));
        }
예제 #9
0
 /// <summary>
 /// Метод добавляющий заначения в словари.
 /// </summary>
 private void AddNewCacheDistance(CachedDistance distance)
 {
     if (!cache.ContainsKey(distance.FromGeoHash))
     {
         cache[distance.FromGeoHash] = new Dictionary <long, CachedDistance>();
     }
     cache[distance.FromGeoHash][distance.ToGeoHash] = distance;
     totalCached++;
 }
예제 #10
0
        bool UpdateFromProvider(CachedDistance distance)
        {
            if (ErrorWays.Any(x => x.FromHash == distance.FromGeoHash && x.ToHash == distance.ToGeoHash))
            {
                //logger.Warn("Повторный запрос дистанции с ошибкой расчета. Пропускаем...");
                return(false);
            }

            //logger.Info("Запрашиваем путь {0}->{1} у сервиса {0}.", distance.FromGeoHash, distance.ToGeoHash, Provider);
            List <PointOnEarth> points = new List <PointOnEarth>();
            double latitude, longitude;

            CachedDistance.GetLatLon(distance.FromGeoHash, out latitude, out longitude);
            points.Add(new PointOnEarth(latitude, longitude));
            CachedDistance.GetLatLon(distance.ToGeoHash, out latitude, out longitude);
            points.Add(new PointOnEarth(latitude, longitude));
            bool ok = false;

            if (Provider == DistanceProvider.Osrm)
            {
                var result = OsrmMain.GetRoute(points, false, GeometryOverview.Full);
                ok = result?.Code == "Ok";
                if (ok && result.Routes.Any())
                {
                    distance.DistanceMeters   = result.Routes.First().TotalDistance;
                    distance.TravelTimeSec    = result.Routes.First().TotalTimeSeconds;
                    distance.PolylineGeometry = result.Routes.First().RouteGeometry;
                }
            }
            else
            {
                var result = SputnikMain.GetRoute(points, false, true);
                ok = result.Status == 0;
                if (ok)
                {
                    distance.DistanceMeters   = result.RouteSummary.TotalDistance;
                    distance.TravelTimeSec    = result.RouteSummary.TotalTimeSeconds;
                    distance.PolylineGeometry = result.RouteGeometry;
                }
            }

            if (ok)
            {
                lock (uow) {
                    AddNewCacheDistance(distance);
                    uow.TrySave(distance);
                    uow.Commit();
                }
                addedCached++;
                return(true);
            }

            ErrorWays.Add(new WayHash(distance.FromGeoHash, distance.ToGeoHash));
            totalErrors++;
            return(false);
        }
예제 #11
0
 /// <summary>
 /// Метод добавляющий заначения в словари.
 /// </summary>
 private void AddNewCacheDistance(CachedDistance distance)
 {
     if (!cache.ContainsKey(distance.FromGeoHash))
     {
         cache[distance.FromGeoHash] = new Dictionary <long, CachedDistance>();
     }
     cache[distance.FromGeoHash][distance.ToGeoHash] = distance;
     totalCached++;
     totalMeters += distance.DistanceMeters;
     totalSec    += distance.TravelTimeSec;
 }
예제 #12
0
        CachedDistance GetCachedGeometry(long fromP, long toP, bool checkDB = true)
        {
            CachedDistance distance = null;
            bool           needAdd  = false;

            //Проверяем в локальном кеше
            if (cache.ContainsKey(fromP) && cache[fromP].ContainsKey(toP))
            {
                distance = cache[fromP][toP];
            }
            //Проверяем в базе данных если разрешено.
            if (distance == null && checkDB)
            {
                IList <CachedDistance> list;
                lock (uow)
                {
                    list = _cachedDistanceRepository.GetCache(uow, new[] { new WayHash(fromP, toP) });
                }
                distance = list.FirstOrDefault();
            }
            //Не нашли создаем новый.
            if (distance == null)
            {
                distance             = new CachedDistance();
                distance.FromGeoHash = fromP;
                distance.ToGeoHash   = toP;
                needAdd = true;
            }
            if (distance.PolylineGeometry == null)
            {
                if (!UpdateFromProvider(distance))
                {
                    return(null);
                }
            }

            if (distance.PolylineGeometry == null)
            {
                return(null);
            }

            if (needAdd)
            {
                lock (uow)
                {
                    AddNewCacheDistance(distance);
                    uow.TrySave(distance);
                    uow.Commit();
                }
            }

            return(distance);
        }
예제 #13
0
 internal static float DistanceCached(this Vector3 from, GameObject target)
 {
     return(MenuManager.IsCacheEnabled ? CachedDistance.Get(@from, target.Position) : @from.Distance(target));
 }
예제 #14
0
        private CachedDistance LoadDistanceFromService(long fromHash, long toHash)
        {
            CachedDistance cachedValue = null;
            bool           ok          = false;

            if (fromHash == toHash)
            {
                cachedValue = new CachedDistance {
                    DistanceMeters = 0,
                    TravelTimeSec  = 0,
                    FromGeoHash    = fromHash,
                    ToGeoHash      = toHash
                };
                AddNewCacheDistance(cachedValue);
                addedCached++;
                ok = true;
            }

            if (!ok)
            {
                List <PointOnEarth> points = new List <PointOnEarth> {
                    CachedDistance.GetPointOnEarth(fromHash),
                    CachedDistance.GetPointOnEarth(toHash)
                };
                if (Provider == DistanceProvider.Osrm)
                {
                    var result = OsrmMain.GetRoute(points, false, GeometryOverview.False);
                    ok = result?.Code == "Ok";
                    if (ok && result.Routes.Any())
                    {
                        cachedValue = new CachedDistance {
                            DistanceMeters = result.Routes.First().TotalDistance,
                            TravelTimeSec  = result.Routes.First().TotalTimeSeconds,
                            FromGeoHash    = fromHash,
                            ToGeoHash      = toHash
                        };
                    }
                }
                else
                {
                    var result = SputnikMain.GetRoute(points, false, false);
                    ok = result.Status == 0;
                    if (ok)
                    {
                        cachedValue = new CachedDistance {
                            DistanceMeters = result.RouteSummary.TotalDistance,
                            TravelTimeSec  = result.RouteSummary.TotalTimeSeconds,
                            FromGeoHash    = fromHash,
                            ToGeoHash      = toHash
                        };
                    }
                }
            }
            if (MultiTaskLoad && ok)
            {
                lock (UoW) {
                    UoW.TrySave(cachedValue as CachedDistance, false);
                    unsavedItems++;
                    if (unsavedItems >= SaveBy)
                    {
                        FlushCache();
                    }
                    AddNewCacheDistance(cachedValue);
                    addedCached++;
                }
                return(cachedValue);
            }
            if (ok)
            {
                AddNewCacheDistance(cachedValue);
                addedCached++;
                return(cachedValue);
            }

            ErrorWays.Add(new WayHash(fromHash, toHash));
            totalErrors++;
            //FIXME Реализовать запрос манхентанского расстояния.
            return(null);
        }
예제 #15
0
 internal static float DistanceCached(this GameObject from, Vector2 target)
 {
     return(MenuManager.IsCacheEnabled
         ? CachedDistance.Get(from.Position, target.To3D())
         : from.Distance(target));
 }
예제 #16
0
 internal static float DistanceCached(this GameObject from, GameObject target)
 {
     return(MenuManager.IsCacheEnabled ? CachedDistance.Get(from, target) : from.Distance(target));
 }
예제 #17
0
 internal static float DistanceCached(this Vector3 from, Vector3 target)
 {
     return(MenuManager.IsCacheEnabled ? CachedDistance.Get(from, target) : from.Distance(target));
 }
예제 #18
0
        // <summary>
        // Метод создаем маршруты на день основываясь на данных всесенных в поля <c>Routes</c>, <c>Orders</c>,
        // <c>Drivers</c> и <c>Forwarders</c>.
        // </summary>
        public void CreateRoutes(TimeSpan drvStartTime, TimeSpan drvEndTime)
        {
            WarningMessages.Clear();
            ProposedRoutes.Clear();             //Очищаем сразу, так как можем выйти из метода ранее.

            logger.Info("Подготавливаем заказы...");
            PerformanceHelper.StartMeasurement($"Строим оптимальные маршруты");

            // Создаем список поездок всех водителей. Тут перебираем всех водителей с машинами
            // и создаем поездки для них, в зависимости от выбранного режима работы.
            var trips = Drivers.Where(x => x.Car != null)
                        .OrderBy(x => x.PriorityAtDay)
                        .SelectMany(drv => drv.DaySchedule != null
                                                                                                ? drv.DaySchedule.Shifts.Where(s => s.StartTime >= drvStartTime && s.StartTime < drvEndTime)
                                    .Select(shift => new PossibleTrip(drv, shift))
                                                                                                                                                : new[] { new PossibleTrip(drv, null) }
                                    )
                        .ToList();

            // Стыкуем уже созданные маршрутные листы с возможными поездками, на основании водителя и смены.
            // Если уже созданный маршрут не найден в поездках, то создаем поездку для него.
            foreach (var existRoute in Routes)
            {
                var trip = trips.FirstOrDefault(x => x.Driver == existRoute.Driver && x.Shift == existRoute.Shift);
                if (trip != null)
                {
                    trip.OldRoute = existRoute;
                }
                else
                {
                    trips.Add(new PossibleTrip(existRoute));
                }
                //Проверяем все ли заказы из МЛ присутствуют в списке заказов. Если их нет. Добавляем.
                foreach (var address in existRoute.Addresses)
                {
                    if (Orders.All(x => x.Id != address.Order.Id))
                    {
                        Orders.Add(address.Order);
                    }
                }
            }

            var possibleRoutes = trips.ToArray();

            if (!possibleRoutes.Any())
            {
                AddWarning("Для построения маршрутов, нет водителей.");
                return;
            }

            TestCars(possibleRoutes);

            var                    areas            = UoW.GetAll <District>().Where(x => x.DistrictsSet.Status == DistrictsSetStatus.Active).ToList();
            List <District>        unusedDistricts  = new List <District>();
            List <CalculatedOrder> calculatedOrders = new List <CalculatedOrder>();

            // Перебираем все заказы, исключаем те которые без координат, определяем для каждого заказа район
            // на основании координат. И создавая экземпляр <c>CalculatedOrder</c>, происходит подсчет сумарной
            // информации о заказе. Всего бутылей, вес и прочее.
            foreach (var order in Orders)
            {
                if (order.DeliveryPoint.Longitude == null || order.DeliveryPoint.Latitude == null)
                {
                    continue;
                }
                var point = new Point((double)order.DeliveryPoint.Latitude.Value, (double)order.DeliveryPoint.Longitude.Value);
                var area  = areas.Find(x => x.DistrictBorder.Contains(point));
                if (area != null)
                {
                    var oldRoute = Routes.FirstOrDefault(r => r.Addresses.Any(a => a.Order.Id == order.Id));
                    if (oldRoute != null)
                    {
                        calculatedOrders.Add(new CalculatedOrder(order, area, false, oldRoute));
                    }
                    else if (possibleRoutes.SelectMany(x => x.Districts).Any(x => x.District.Id == area.Id))
                    {
                        var cOrder = new CalculatedOrder(order, area);
                        //if(possibleRoutes.Any(r => r.GeographicGroup.Id == cOrder.ShippingBase.Id))//убрать, если в автоформировании должны учавствовать заказы из всех частей города вне зависимости от того какие части города выбраны в диалоге
                        calculatedOrders.Add(cOrder);
                    }
                    else if (!unusedDistricts.Contains(area))
                    {
                        unusedDistricts.Add(area);
                    }
                }
            }
            Nodes = calculatedOrders.ToArray();
            if (unusedDistricts.Any())
            {
                AddWarning("Районы без водителей: {0}", string.Join(", ", unusedDistricts.Select(x => x.DistrictName)));
            }

            // Создаем калькулятор расчета расстояний. Он сразу запрашивает уже имеющиеся расстояния из кеша
            // и в фоновом режиме начинает считать недостающую матрицу.
            distanceCalculator = new ExtDistanceCalculator(DistanceProvider.Osrm, Nodes.Select(x => x.Order.DeliveryPoint).ToArray(), StatisticsTxtAction);

            logger.Info("Развозка по {0} районам.", calculatedOrders.Select(x => x.District).Distinct().Count());
            PerformanceHelper.AddTimePoint(logger, $"Подготовка заказов");

            // Пред запуском оптимизации мы должны создать модель и внести в нее все необходимые данные.
            logger.Info("Создаем модель...");
            RoutingModel routing = new RoutingModel(Nodes.Length + 1, possibleRoutes.Length, 0);

            // Создаем измерение со временем на маршруте.
            // <c>horizon</c> - ограничивает максимально допустимое значение диапазона, чтобы не уйти за границы суток;
            // <c>maxWaitTime</c> - Максимальное время ожидания водителя. То есть водитель закончил разгрузку следующий
            // адрес в маршруте у него не должен быть позже чем на 4 часа ожидания.
            int horizon        = 24 * 3600;
            int maxWaitTime    = 4 * 3600;
            var timeEvaluators = possibleRoutes.Select(x => new CallbackTime(Nodes, x, distanceCalculator)).ToArray();

            routing.AddDimensionWithVehicleTransits(timeEvaluators, maxWaitTime, horizon, false, "Time");
            var time_dimension = routing.GetDimensionOrDie("Time");

            // Ниже заполняем все измерения для учета бутылей, веса, адресов, объема.
            var bottlesCapacity = possibleRoutes.Select(x => (long)x.Car.MaxBottles).ToArray();

            routing.AddDimensionWithVehicleCapacity(new CallbackBottles(Nodes), 0, bottlesCapacity, true, "Bottles");

            var weightCapacity = possibleRoutes.Select(x => (long)x.Car.MaxWeight).ToArray();

            routing.AddDimensionWithVehicleCapacity(new CallbackWeight(Nodes), 0, weightCapacity, true, "Weight");

            var volumeCapacity = possibleRoutes.Select(x => (long)(x.Car.MaxVolume * 1000)).ToArray();

            routing.AddDimensionWithVehicleCapacity(new CallbackVolume(Nodes), 0, volumeCapacity, true, "Volume");

            var addressCapacity = possibleRoutes.Select(x => (long)(x.Driver.MaxRouteAddresses)).ToArray();

            routing.AddDimensionWithVehicleCapacity(new CallbackAddressCount(Nodes.Length), 0, addressCapacity, true, "AddressCount");

            var bottlesDimension = routing.GetDimensionOrDie("Bottles");
            var addressDimension = routing.GetDimensionOrDie("AddressCount");

            for (int ix = 0; ix < possibleRoutes.Length; ix++)
            {
                // Устанавливаем функцию получения стоимости маршрута.
                routing.SetArcCostEvaluatorOfVehicle(new CallbackDistanceDistrict(Nodes, possibleRoutes[ix], distanceCalculator), ix);

                // Добавляем фиксированный штраф за принадлежность водителя.
                if (possibleRoutes[ix].Driver.DriverType.HasValue)
                {
                    routing.SetFixedCostOfVehicle(((int)possibleRoutes[ix].Driver.DriverType) * DriverPriorityPenalty, ix);
                }
                else
                {
                    routing.SetFixedCostOfVehicle(DriverPriorityPenalty * 3, ix);
                }

                var cumulTimeOnEnd   = routing.CumulVar(routing.End(ix), "Time");
                var cumulTimeOnBegin = routing.CumulVar(routing.Start(ix), "Time");

                // Устанавливаем минимальные(мягкие) границы для измерений. При значениях меньше минимальных, маршрут все таки принимается,
                // но вносятся некоторые штрафные очки на последнюю точку маршрута.
                //bottlesDimension.SetEndCumulVarSoftLowerBound(ix, possibleRoutes[ix].Car.MinBottles, MinBottlesInRoutePenalty);
                //addressDimension.SetEndCumulVarSoftLowerBound(ix, possibleRoutes[ix].Driver.MinRouteAddresses, MinAddressesInRoutePenalty);

                // Устанавливаем диапазон времени для движения по маршруту в зависимости от выбраной смены,
                // день, вечер и с учетом досрочного завершения водителем работы.
                if (possibleRoutes[ix].Shift != null)
                {
                    var shift   = possibleRoutes[ix].Shift;
                    var endTime = possibleRoutes[ix].EarlyEnd.HasValue
                                                                                                        ? Math.Min(shift.EndTime.TotalSeconds, possibleRoutes[ix].EarlyEnd.Value.TotalSeconds)
                                                                                                        : shift.EndTime.TotalSeconds;
                    cumulTimeOnEnd.SetMax((long)endTime);
                    cumulTimeOnBegin.SetMin((long)shift.StartTime.TotalSeconds);
                }
                else if (possibleRoutes[ix].EarlyEnd.HasValue)                //Устанавливаем время окончания рабочего дня у водителя.
                {
                    cumulTimeOnEnd.SetMax((long)possibleRoutes[ix].EarlyEnd.Value.TotalSeconds);
                }
            }

            for (int ix = 0; ix < Nodes.Length; ix++)
            {
                // Проставляем на каждый адрес окно времени приезда.
                var startWindow = Nodes[ix].Order.DeliverySchedule.From.TotalSeconds;
                var endWindow   = Nodes[ix].Order.DeliverySchedule.To.TotalSeconds - Nodes[ix].Order.CalculateTimeOnPoint(false);               //FIXME Внимание здесь задаем время без экспедитора и без учета скорости водителя. Это не правильно, но другого варианта я придумать не смог.
                if (endWindow < startWindow)
                {
                    AddWarning("Время разгрузки на {2}, не помещается в диапазон времени доставки. {0}-{1}", Nodes[ix].Order.DeliverySchedule.From, Nodes[ix].Order.DeliverySchedule.To, Nodes[ix].Order.DeliveryPoint.ShortAddress);
                    endWindow = startWindow;
                }
                time_dimension.CumulVar(ix + 1).SetRange((long)startWindow, (long)endWindow);
                // Добавляем абсолютно все заказы в дизюкцию. Если бы заказы небыли вдобавлены в отдельные дизьюкции
                // то при не возможность доставить хоть один заказ. Все решение бы считаль не верным. Добавление каждого заказа
                // в отдельную дизьюкцию, позволяет механизму не вести какой то и заказов, и все таки формировать решение с недовезенными
                // заказами. Дизьюкция работает так. Он говорит, если хотя бы один заказ в этой группе(дизьюкции) доставлен,
                // то все хорошо, иначе штраф. Так как у нас в кадой дизьюкции по одному заказу. Мы получаем опциональную доставку каждого заказа.
                routing.AddDisjunction(new int[] { ix + 1 }, MaxDistanceAddressPenalty);
            }

            logger.Debug("Nodes.Length = {0}", Nodes.Length);
            logger.Debug("routing.Nodes() = {0}", routing.Nodes());
            logger.Debug("GetNumberOfDisjunctions = {0}", routing.GetNumberOfDisjunctions());

            RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters();

            // Setting first solution heuristic (cheapest addition).
            // Указывается стратегия первоначального заполнения. Опытным путем было вычислено, что именно при
            // стратегиях вставки маршруты получаются с набором точек более близких к друг другу. То есть в большей
            // степени облачком. Что воспринималось человеком как более отпимальное. В отличии от большенства других
            // стратегий в которых маршруты, формируюся скорее по лентами ведущими через все обезжаемые раоны. То есть водители
            // чаще имели пересечения маршутов.
            search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.ParallelCheapestInsertion;

            search_parameters.TimeLimitMs = MaxTimeSeconds * 1000;
            // Отключаем внутреннего кеширования расчитанных значений. Опытным путем было проверено, что включение этого значения.
            // Значительно(на несколько секунд) увеличивает время закрытия модели и сокращает иногда не значительно время расчета оптимизаций.
            // И в принцепе становится целесообразно только на количествах заказов 300-400. При количестве заказов менее 200
            // влючение отпечатков значений. Не уменьшало, а увеличивало общее время расчета. А при большом количестве заказов
            // время расчета уменьшалось не значительно.
            //search_parameters.FingerprintArcCostEvaluators = false;

            search_parameters.FingerprintArcCostEvaluators = true;

            //search_parameters.OptimizationStep = 100;

            var solver = routing.solver();

            PerformanceHelper.AddTimePoint(logger, $"Настроили оптимизацию");
            logger.Info("Закрываем модель...");

            if (
                WarningMessages.Any() && !interactiveService.Question(
                    string.Join("\n", WarningMessages.Select(x => "⚠ " + x)),
                    "При построении транспортной модели обнаружены следующие проблемы:\n{0}\nПродолжить?"
                    )
                )
            {
                distanceCalculator.Canceled = true;
                distanceCalculator.Dispose();
                return;
            }

            logger.Info("Рассчет расстояний между точками...");
            routing.CloseModelWithParameters(search_parameters);
#if DEBUG
            PrintMatrixCount(distanceCalculator.matrixcount);
#endif
            //Записывем возможно не схраненый кеш в базу.
            distanceCalculator.FlushCache();
            //Попытка хоть как то ослеживать что происходит в момент построения. Возможно не очень правильная.
            //Пришлось создавать 2 монитора.
            var lastSolution = solver.MakeLastSolutionCollector();
            lastSolution.AddObjective(routing.CostVar());
            routing.AddSearchMonitor(lastSolution);
            routing.AddSearchMonitor(new CallbackMonitor(solver, StatisticsTxtAction, lastSolution));

            PerformanceHelper.AddTimePoint(logger, $"Закрыли модель");
            logger.Info("Поиск решения...");

            Assignment solution = routing.SolveWithParameters(search_parameters);
            PerformanceHelper.AddTimePoint(logger, $"Получили решение.");
            logger.Info("Готово. Заполняем.");
#if DEBUG
            PrintMatrixCount(distanceCalculator.matrixcount);
#endif
            Console.WriteLine("Status = {0}", routing.Status());
            if (solution != null)
            {
                // Solution cost.
                Console.WriteLine("Cost = {0}", solution.ObjectiveValue());
                time_dimension = routing.GetDimensionOrDie("Time");

                //Читаем полученные маршруты.
                for (int route_number = 0; route_number < routing.Vehicles(); route_number++)
                {
                    var  route       = new ProposedRoute(possibleRoutes[route_number]);
                    long first_node  = routing.Start(route_number);
                    long second_node = solution.Value(routing.NextVar(first_node));                     // Пропускаем первый узел, так как это наша база.
                    route.RouteCost = routing.GetCost(first_node, second_node, route_number);

                    while (!routing.IsEnd(second_node))
                    {
                        var time_var = time_dimension.CumulVar(second_node);
                        var rPoint   = new ProposedRoutePoint(
                            TimeSpan.FromSeconds(solution.Min(time_var)),
                            TimeSpan.FromSeconds(solution.Max(time_var)),
                            Nodes[second_node - 1].Order
                            );
                        rPoint.DebugMaxMin = string.Format("\n({0},{1})[{3}-{4}]-{2} Cost:{5}",
                                                           new DateTime().AddSeconds(solution.Min(time_var)).ToShortTimeString(),
                                                           new DateTime().AddSeconds(solution.Max(time_var)).ToShortTimeString(),
                                                           second_node,
                                                           rPoint.Order.DeliverySchedule.From.ToString("hh\\:mm"),
                                                           rPoint.Order.DeliverySchedule.To.ToString("hh\\:mm"),
                                                           routing.GetCost(first_node, second_node, route_number)
                                                           );
                        route.Orders.Add(rPoint);

                        first_node       = second_node;
                        second_node      = solution.Value(routing.NextVar(first_node));
                        route.RouteCost += routing.GetCost(first_node, second_node, route_number);
                    }

                    if (route.Orders.Count > 0)
                    {
                        ProposedRoutes.Add(route);
                        logger.Debug("Маршрут {0}: {1}",
                                     route.Trip.Driver.ShortName,
                                     string.Join(" -> ", route.Orders.Select(x => x.DebugMaxMin))
                                     );
                    }
                    else
                    {
                        logger.Debug("Маршрут {0}: пустой", route.Trip.Driver.ShortName);
                    }
                }
            }

#if DEBUG
            logger.Debug("SGoToBase:{0}", string.Join(", ", CallbackDistanceDistrict.SGoToBase.Select(x => $"{x.Key.Driver.ShortName}={x.Value}")));
            logger.Debug("SFromExistPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SFromExistPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}")));
            logger.Debug("SUnlikeDistrictPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SUnlikeDistrictPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}")));
            logger.Debug("SLargusPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SLargusPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}")));
#endif

            if (ProposedRoutes.Count > 0)
            {
                logger.Info($"Предложено {ProposedRoutes.Count} маршрутов.");
            }
            PerformanceHelper.Main.PrintAllPoints(logger);

            if (distanceCalculator.ErrorWays.Any())
            {
                logger.Debug("Ошибок получения расстояний {0}", distanceCalculator.ErrorWays.Count);
                var uniqueFrom = distanceCalculator.ErrorWays.Select(x => x.FromHash).Distinct().ToList();
                var uniqueTo   = distanceCalculator.ErrorWays.Select(x => x.ToHash).Distinct().ToList();
                logger.Debug("Уникальных точек: отправки = {0}, прибытия = {1}", uniqueFrom.Count, uniqueTo.Count);
                logger.Debug("Проблемные точки отправки:\n{0}",
                             string.Join("; ", distanceCalculator.ErrorWays
                                         .GroupBy(x => x.FromHash)
                                         .Where(x => x.Count() > (uniqueTo.Count / 2))
                                         .Select(x => CachedDistance.GetText(x.Key)))
                             );
                logger.Debug("Проблемные точки прибытия:\n{0}",
                             string.Join("; ", distanceCalculator.ErrorWays
                                         .GroupBy(x => x.ToHash)
                                         .Where(x => x.Count() > (uniqueFrom.Count / 2))
                                         .Select(x => CachedDistance.GetText(x.Key)))
                             );
            }
        }
예제 #19
0
        // <summary>
        // Получаем предложение по оптимальному расположению адресов в указанном маршруте.
        // Рачет идет с учетом окон доставки. Но естественно без любых ограничений по весу и прочему.
        // </summary>
        // <returns>Предолженый маршрут</returns>
        // <param name="route">Первоначальный маршрутный лист, чтобы взять адреса.</param>
        public ProposedRoute RebuidOneRoute(RouteList route)
        {
            var trip = new PossibleTrip(route);

            logger.Info("Подготавливаем заказы...");
            PerformanceHelper.StartMeasurement($"Строим маршрут");

            List <CalculatedOrder> calculatedOrders = new List <CalculatedOrder>();

            foreach (var address in route.Addresses)
            {
                if (address.Order.DeliveryPoint.Longitude == null || address.Order.DeliveryPoint.Latitude == null)
                {
                    continue;
                }

                calculatedOrders.Add(new CalculatedOrder(address.Order, null));
            }
            Nodes = calculatedOrders.ToArray();

            distanceCalculator = new ExtDistanceCalculator(DistanceProvider.Osrm, Nodes.Select(x => x.Order.DeliveryPoint).ToArray(), StatisticsTxtAction);

            PerformanceHelper.AddTimePoint(logger, $"Подготовка заказов");

            logger.Info("Создаем модель...");
            RoutingModel routing = new RoutingModel(Nodes.Length + 1, 1, 0);

            int horizon = 24 * 3600;

            routing.AddDimension(new CallbackTime(Nodes, trip, distanceCalculator), 3 * 3600, horizon, false, "Time");
            var time_dimension = routing.GetDimensionOrDie("Time");

            var cumulTimeOnEnd   = routing.CumulVar(routing.End(0), "Time");
            var cumulTimeOnBegin = routing.CumulVar(routing.Start(0), "Time");

            if (route.Shift != null)
            {
                var shift = route.Shift;
                cumulTimeOnEnd.SetMax((long)shift.EndTime.TotalSeconds);
                cumulTimeOnBegin.SetMin((long)shift.StartTime.TotalSeconds);
            }

            routing.SetArcCostEvaluatorOfVehicle(new CallbackDistance(Nodes, distanceCalculator), 0);

            for (int ix = 0; ix < Nodes.Length; ix++)
            {
                var startWindow = Nodes[ix].Order.DeliverySchedule.From.TotalSeconds;
                var endWindow   = Nodes[ix].Order.DeliverySchedule.To.TotalSeconds - trip.Driver.TimeCorrection(Nodes[ix].Order.CalculateTimeOnPoint(route.Forwarder != null));
                if (endWindow < startWindow)
                {
                    logger.Warn("Время разгрузки на точке, не помещается в диапазон времени доставки. {0}-{1}", Nodes[ix].Order.DeliverySchedule.From, Nodes[ix].Order.DeliverySchedule.To);
                    endWindow = startWindow;
                }
                time_dimension.CumulVar(ix + 1).SetRange((long)startWindow, (long)endWindow);
                routing.AddDisjunction(new int[] { ix + 1 }, MaxDistanceAddressPenalty);
            }

            RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters();

            search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.ParallelCheapestInsertion;

            search_parameters.TimeLimitMs = MaxTimeSeconds * 1000;
            //search_parameters.FingerprintArcCostEvaluators = true;
            //search_parameters.OptimizationStep = 100;

            var solver = routing.solver();

            PerformanceHelper.AddTimePoint(logger, $"Настроили оптимизацию");
            logger.Info("Закрываем модель...");
            logger.Info("Рассчет расстояний между точками...");
            routing.CloseModelWithParameters(search_parameters);
            distanceCalculator.FlushCache();
            var lastSolution = solver.MakeLastSolutionCollector();

            lastSolution.AddObjective(routing.CostVar());
            routing.AddSearchMonitor(lastSolution);
            routing.AddSearchMonitor(new CallbackMonitor(solver, StatisticsTxtAction, lastSolution));

            PerformanceHelper.AddTimePoint(logger, $"Закрыли модель");
            logger.Info("Поиск решения...");

            Assignment solution = routing.SolveWithParameters(search_parameters);

            PerformanceHelper.AddTimePoint(logger, $"Получили решение.");
            logger.Info("Готово. Заполняем.");
            Console.WriteLine("Status = {0}", routing.Status());
            ProposedRoute proposedRoute = null;

            if (solution != null)
            {
                // Solution cost.
                Console.WriteLine("Cost = {0}", solution.ObjectiveValue());
                time_dimension = routing.GetDimensionOrDie("Time");

                int route_number = 0;

                proposedRoute = new ProposedRoute(null);
                long first_node  = routing.Start(route_number);
                long second_node = solution.Value(routing.NextVar(first_node));                 // Пропускаем первый узел, так как это наша база.
                proposedRoute.RouteCost = routing.GetCost(first_node, second_node, route_number);

                while (!routing.IsEnd(second_node))
                {
                    var time_var = time_dimension.CumulVar(second_node);
                    var rPoint   = new ProposedRoutePoint(
                        TimeSpan.FromSeconds(solution.Min(time_var)),
                        TimeSpan.FromSeconds(solution.Max(time_var)),
                        Nodes[second_node - 1].Order
                        );
                    rPoint.DebugMaxMin = string.Format("\n({0},{1})[{3}-{4}]-{2}",
                                                       new DateTime().AddSeconds(solution.Min(time_var)).ToShortTimeString(),
                                                       new DateTime().AddSeconds(solution.Max(time_var)).ToShortTimeString(),
                                                       second_node,
                                                       rPoint.Order.DeliverySchedule.From.ToString("hh\\:mm"),
                                                       rPoint.Order.DeliverySchedule.To.ToString("hh\\:mm")
                                                       );
                    proposedRoute.Orders.Add(rPoint);

                    first_node               = second_node;
                    second_node              = solution.Value(routing.NextVar(first_node));
                    proposedRoute.RouteCost += routing.GetCost(first_node, second_node, route_number);
                }
            }

            PerformanceHelper.Main.PrintAllPoints(logger);

            if (distanceCalculator.ErrorWays.Count > 0)
            {
                logger.Debug("Ошибок получения расстояний {0}", distanceCalculator.ErrorWays.Count);
                var uniqueFrom = distanceCalculator.ErrorWays.Select(x => x.FromHash).Distinct().ToList();
                var uniqueTo   = distanceCalculator.ErrorWays.Select(x => x.ToHash).Distinct().ToList();
                logger.Debug("Уникальных точек: отправки = {0}, прибытия = {1}", uniqueFrom.Count, uniqueTo.Count);
                logger.Debug("Проблемные точки отправки:\n{0}",
                             string.Join("; ", distanceCalculator.ErrorWays
                                         .GroupBy(x => x.FromHash)
                                         .Where(x => x.Count() > (uniqueTo.Count / 2))
                                         .Select(x => CachedDistance.GetText(x.Key)))
                             );
                logger.Debug("Проблемные точки прибытия:\n{0}",
                             string.Join("; ", distanceCalculator.ErrorWays
                                         .GroupBy(x => x.ToHash)
                                         .Where(x => x.Count() > (uniqueFrom.Count / 2))
                                         .Select(x => CachedDistance.GetText(x.Key)))
                             );
            }
            distanceCalculator.Dispose();
            return(proposedRoute);
        }
예제 #20
0
        public string GetDataText()
        {
            string OutputText = "SiiNunit\r\n{\r\n";

            OutputText += "user_profile : " + UserProfileNameless + " {\r\n";
            OutputText += " face: " + Face.ToString() + "\r\n";
            OutputText += " brand: " + Brand + "\r\n";
            OutputText += " map_path: \"" + MapPath + "\"\r\n";
            OutputText += " logo: " + Logo + "\r\n";
            OutputText += " company_name: " + CompanyName + "\r\n";
            OutputText += " male: " + GenederMale.ToString().ToLower() + "\r\n";
            OutputText += " cached_experience: " + CachedExperiencePoints.ToString() + "\r\n";
            OutputText += " cached_distance: " + CachedDistance.ToString() + "\r\n";
            OutputText += " user_data: 17";
            OutputText += " user_data[0]: " + string.IsNullOrEmpty(SomeTimeUD0.ToString()) ?? "\"\"" + "\r\n";
            OutputText += " user_data[1]: \"" + string.IsNullOrEmpty(LicensePlateUD1) + "\"\r\n";
            OutputText += " user_data[2]: " + string.IsNullOrEmpty(SomeCheckSumUD2) ?? "\"\"" + "\r\n";
            OutputText += " user_data[3]: " + string.IsNullOrEmpty(WoTConnectedUD3.ToString()) ?? "\"\"" + "\r\n";
            OutputText += " user_data[4]: \"" + RoadsExploredUD4.ToString() + "\"\r\n";
            OutputText += " user_data[5]: " + DeliveriesFinishedUD5.ToString() + "\r\n";
            OutputText += " user_data[6]: " + OwnedTrucksUD6.ToString() + "\r\n";
            OutputText += " user_data[7]: " + OwnedGaradesSmallUD7.ToString() + "\r\n";
            OutputText += " user_data[8]: " + OwnedGaradesLargeUD8.ToString() + "\r\n";
            OutputText += " user_data[9]: " + GameTimeSpentUD9.ToString() + "\r\n";
            OutputText += " user_data[10]: " + RealTimeSpentUD10.ToString() + "\r\n";
            OutputText += " user_data[11]: \"" + CurrentTruckUD11 + "\"";
            OutputText += " user_data[12]: \"" + string.Join(",", OwnedTruckListUD12) + "\"";
            OutputText += " user_data[13]: " + string.IsNullOrEmpty(SomeUserDataUD13) ?? "\"\"" + "\r\n";
            OutputText += " user_data[14]: " + string.IsNullOrEmpty(SomeUserDataUD14.ToString()) ?? "\"\"" + "\r\n";
            OutputText += " user_data[15]: " + string.IsNullOrEmpty(SomeUserDataUD15) ?? "\"\"" + "\r\n";
            OutputText += " user_data[16]: " + OwnedTrailersUD16.ToString() + "\r\n";

            OutputText += " active_mods: " + ActiveMods.Capacity.ToString() + "\r\n";
            for (int i = 0; i < ActiveMods.Capacity; i++)
            {
                OutputText += " active_mods[" + i.ToString() + "]: " + ActiveMods[i].ToString() + "\r\n";
            }

            OutputText += " customization: " + Customization.ToString() + "\r\n";

            OutputText += " cached_stats: " + CachedStats.Capacity.ToString() + "\r\n";
            for (int i = 0; i < CachedStats.Capacity; i++)
            {
                OutputText += " cached_stats[" + i.ToString() + "]: " + CachedStats[i].ToString() + "\r\n";
            }

            OutputText += " cached_discovery: " + CachedDiscovery.Capacity.ToString() + "\r\n";
            for (int i = 0; i < CachedDiscovery.Capacity; i++)
            {
                OutputText += " cached_discovery[" + i.ToString() + "]: " + CachedDiscovery[i].ToString() + "\r\n";
            }

            OutputText += " version: " + Version.ToString() + "\r\n";
            OutputText += " online_user_name: " + OnlineUserName + "\r\n";
            OutputText += " online_password: "******"\r\n";
            OutputText += " profile_name: " + ProfileName + "\r\n";
            OutputText += " creation_time: " + CreationTime.ToString() + "\r\n";
            OutputText += " save_time: " + SaveTime.ToString() + "\r\n";

            OutputText += "}\r\n\r\n}";

            return(OutputText);
        }
예제 #21
0
 internal static float DistanceCached(this Vector2 from, Vector2 target)
 {
     return(MenuManager.IsCacheEnabled ? CachedDistance.Get(@from.To3D(), target.To3D()) : @from.Distance(target));
 }