Beispiel #1
0
        /// <summary>
        /// Tries to Solve the <paramref name="context"/> <see cref="RoutingContext.Model"/>
        /// given <paramref name="searchParams"/>. Receives the <paramref name="solution"/>
        /// prior to response.
        /// </summary>
        /// <param name="context">The Context whose Model is being Solved. Assumes that the
        /// <see cref="Visitors"/> have all been applied and any <see cref="Context"/> mutations
        /// that were going to occur have all occurred. So, should be safe now to add dimensions
        /// to the <see cref="RoutingContext.Model"/>.</param>
        /// <param name="searchParams">The SearchParameters influencing the
        /// solution.</param>
        /// <param name="solution">Receives the Solution on response.</param>
        /// <returns>Whether a <paramref name="solution"/> was properly received.</returns>
        protected virtual bool TrySolve(TContext context, RoutingSearchParameters searchParams, out Assignment solution)
        {
            this.AddDimensions(context);

            var model = context.Model;

            solution = null;

            try
            {
                // TODO: TBD: is there a way to probe for status during and/or before/after the search itself?
                solution = searchParams == null
                    ? model.Solve()
                    : model.SolveWithParameters(searchParams);

                /* TODO: TBD: we can capture "debug" output from the solver,
                 * but as far as we can determine this requires/assumes there
                 * was a solution obtained from the solver in the first place.
                 * When solution is null, then what? */
            }
            catch //(Exception ex)
            {
                // TODO: TBD: potentially log any exceptions...
            }

            return(solution != null);
        }
Beispiel #2
0
        public Assignment TryGetSolution(RoutingSearchParameters searchParameters)
        {
            Assignment solution = null;

            if (searchParameters == null)
            {
                searchParameters = GetDefaultSearchParameters();
            }
            //for loop that tries to find the earliest feasible solution (trying to minimize the maximum upper bound) within a maximum delay delivery time (upper bound), using the current customer requests
            for (int currentMaximumDelayTime = 0; currentMaximumDelayTime < DataModel.MaxAllowedDeliveryDelayTime; currentMaximumDelayTime = currentMaximumDelayTime + 60) //iterates adding 1 minute to maximum allowed timeWindow (60 seconds) if a feasible solution isnt found for the current upperbound
            {
                MaximumDeliveryDelayTime = currentMaximumDelayTime;
                Init();
                //Get the solution of the problem
                try
                {
                    solution = RoutingModel.SolveWithParameters(searchParameters);
                }
                catch (Exception)
                {
                    solution = null;
                }

                if (solution != null) //if true, solution was found, breaks the cycle
                {
                    break;
                }
            }

            Console.WriteLine("Solver status:" + GetSolverStatus());
            return(solution); //retuns null if no solution is found, otherwise returns the solution
        }
Beispiel #3
0
        /// <summary>
        /// Invokes the <see cref="ConfigureSearchParameters"/>.
        /// </summary>
        /// <param name="searchParams"></param>
        protected virtual void OnConfigureSearchParameters(ref RoutingSearchParameters searchParams)
        {
            var args = new RoutingSearchParametersEventArgs(searchParams);

            this.ConfigureSearchParameters?.Invoke(this, args);
            searchParams = args.Parameters;
        }
Beispiel #4
0
        public void TestTransitMatrix()
        {
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/);
            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);

            // Create a distance callback.
            long[][] matrix = new long[][] {
                new long[] { 1, 1, 1, 1, 1 },
                new long[] { 1, 1, 1, 1, 1 },
                new long[] { 1, 1, 1, 1, 1 },
                new long[] { 1, 1, 1, 1, 1 },
                new long[] { 1, 1, 1, 1, 1 },
            };
            int transitCallbackIndex = routing.RegisterTransitMatrix(matrix);

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5
            Assert.Equal(5, solution.ObjectiveValue());
        }
Beispiel #5
0
        private RoutingSearchParameters CreateSearchParameters()
        {
            RoutingSearchParameters searchParameters = operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
            return(searchParameters);
        }
Beispiel #6
0
        public void TestVectorDimension()
        {
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/);
            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);

            // Create a distance callback.
            long[]      vector = new long[] { 1, 1, 1, 1, 1 };
            IntBoolPair result = routing.AddVectorDimension(
                vector,
                /*capacity=*/ 10,
                /*fix_start_cumul_to_zero=*/ true,
                "Dimension");

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(result.first);
            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+1)-> 0 := +5
            Assert.Equal(5, solution.ObjectiveValue());
        }
        public void SimpleLambdaCallback(bool callGC)
        {
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(
                5 /*locations*/, 1 /*vehicle*/, 0 /*depot*/);
            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);
            // Create a distance callback.
            int transitCallbackIndex = routing.RegisterTransitCallback(
                (long fromIndex, long toIndex) => {
                // Convert from routing variable Index to distance matrix NodeIndex.
                var fromNode = manager.IndexToNode(fromIndex);
                var toNode   = manager.IndexToNode(toIndex);
                return(Math.Abs(toNode - fromNode));
            });

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
            if (callGC)
            {
                GC.Collect();
            }
            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy =
                FirstSolutionStrategy.Types.Value.PathCheapestArc;
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // 0 --(+1)-> 1 --(+1)-> 2 --(+1)-> 3 --(+1)-> 4 --(+4)-> 0 := +8
            Assert.Equal(8, solution.ObjectiveValue());
        }
        private void SortUpdate()
        {
            var t0 = Stopwatch.GetTimestamp();

            try {
                if (this.rdoNoSort.Checked)
                {
                    this.Log("==== No Sort ====");
                    this.NoSort();
                }
                else if (this.rdoNearestNeighbor.Checked)
                {
                    this.Log("==== Nearest Neighbor ====");
                    this.SortNearestNeighbor();
                }
                else if (this.rdo2Opt.Checked)
                {
                    this.Log("==== 2-OPT ====");
                    this.NoSort();
                    this.Improve2Opt();
                }
                else if (this.rdo2OptNative.Checked)
                {
                    this.Log("==== 2-OPT ====");
                    this.NoSort();
                    AlgDll.Improve2Opt(this.visitOrder, this.visitOrder.Length, this.distTable);
                }
                else if (this.rdoNearestNeighbor2Opt.Checked)
                {
                    this.Log("==== Nearest Neighbor + 2-OPT====");
                    this.SortNearestNeighbor();
                    this.Improve2Opt();
                }
                else if (this.rdoNearestNeighbor2OptNative.Checked)
                {
                    this.Log("==== Nearest Neighbor + 2-OPT====");
                    this.SortNearestNeighbor();
                    AlgDll.Improve2Opt(this.visitOrder, this.visitOrder.Length, this.distTable);
                }
                else if (this.rdoGoogleRoute.Checked)
                {
                    string msg = string.Format("==== Google Route ====");
                    this.Log(msg);
                    RoutingSearchParameters srcPrms = (RoutingSearchParameters)this.grdPrm.SelectedObject;
                    this.SortGoogleRoute(srcPrms);
                }
            } catch (Exception ex) {
                this.visitOrder = new int[0];
                this.Log(ex.ToString());
            }

            var ms = (Stopwatch.GetTimestamp() - t0) / (double)Stopwatch.Frequency * 1000;

            float calcDist = CalcRouteDist(this.visitOrder);

            this.Log(string.Format("Route Dist : {0}", calcDist));
            this.Log(string.Format("Calc Time  : {0}ms", ms));

            this.pbxDraw.Invalidate();
        }
Beispiel #9
0
    static void Solve(int size, int forbidden, int seed)
    {
        RoutingIndexManager manager = new RoutingIndexManager(size, 1, 0);
        RoutingModel        routing = new RoutingModel(manager);

        // Setting the cost function.
        // Put a permanent callback to the distance accessor here. The callback
        // has the following signature: ResultCallback2<int64_t, int64_t, int64_t>.
        // The two arguments are the from and to node inidices.
        RandomManhattan distances = new RandomManhattan(manager, size, seed);

        routing.SetArcCostEvaluatorOfAllVehicles(routing.RegisterTransitCallback(distances.Call));

        // Forbid node connections (randomly).
        Random randomizer            = new Random();
        long   forbidden_connections = 0;

        while (forbidden_connections < forbidden)
        {
            long from = randomizer.Next(size - 1);
            long to   = randomizer.Next(size - 1) + 1;
            if (routing.NextVar(from).Contains(to))
            {
                Console.WriteLine("Forbidding connection {0} -> {1}", from, to);
                routing.NextVar(from).RemoveValue(to);
                ++forbidden_connections;
            }
        }

        // Add dummy dimension to test API.
        routing.AddDimension(routing.RegisterUnaryTransitCallback((long index) =>
                                                                  { return(1); }),
                             size + 1, size + 1, true, "dummy");

        // Solve, returns a solution if any (owned by RoutingModel).
        RoutingSearchParameters search_parameters =
            operations_research_constraint_solver.DefaultRoutingSearchParameters();

        // Setting first solution heuristic (cheapest addition).
        search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;

        Assignment solution = routing.SolveWithParameters(search_parameters);

        Console.WriteLine("Status = {0}", routing.GetStatus());
        if (solution != null)
        {
            // Solution cost.
            Console.WriteLine("Cost = {0}", solution.ObjectiveValue());
            // Inspect solution.
            // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1
            int route_number = 0;
            for (long node = routing.Start(route_number); !routing.IsEnd(node);
                 node = solution.Value(routing.NextVar(node)))
            {
                Console.Write("{0} -> ", node);
            }
            Console.WriteLine("0");
        }
    }
        public FormMain()
        {
            InitializeComponent();
            RoutingSearchParameters srcPrms = operations_research_constraint_solver.DefaultRoutingSearchParameters();

            this.grdPrm.SelectedObject = srcPrms;
            this.pbxDraw.ZoomToRect(0, 0, bw, bh);
        }
Beispiel #11
0
        void TimeLimits()
        {
            RoutingSearchParameters searchParameters = operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.TimeLimit = new Duration {
                Seconds = 10
            };
        }
Beispiel #12
0
        public RoutingDataModel CreateInitialSimulationDataModel(bool allowDropNodes, Simulation.Simulation simulation)
        {
            var numberCustomers = simulation.Params.NumberInitialRequests;
            var numberVehicles  = simulation.Params.VehicleNumber;

            Console.WriteLine(this.ToString() + "Creating new random DataModel for " + numberVehicles + " Vehicles and " + numberCustomers + " Customers, AllowDropNodes: " + allowDropNodes);
GenerateNewDataModelLabel:
            List <Vehicle> dataModelVehicles   = new List <Vehicle>();
            List <long> startDepotsArrivalTime = new List <long>(numberVehicles);

            //Creates two available vehicles to be able to perform flexible routing for the pdtwdatamodel
            for (int i = 0; i < numberVehicles; i++)
            {
                var vehicle = new Vehicle(simulation.Params.VehicleSpeed, simulation.Params.VehicleCapacity, simulation.Context.Depot, simulation.Context.Depot);
                dataModelVehicles.Add(vehicle);
                startDepotsArrivalTime.Add(0);//startDepotArrival time  = 0 for all the vehicles
            }

            var         customersToBeServed = new List <Customer>();
            List <Stop> excludedStops       = new List <Stop>();

            foreach (var vehicle in dataModelVehicles)
            {
                if (!excludedStops.Contains(vehicle.StartStop))
                {
                    excludedStops.Add(vehicle.StartStop);
                }

                if (!excludedStops.Contains(vehicle.EndStop))
                {
                    excludedStops.Add(vehicle.EndStop);
                }
            }

            for (int i = 0; i < numberCustomers; i++) //generate 5 customers with random timeWindows and random pickup and delivery stops
            {
                var requestTime      = 0;
                var pickupTimeWindow = new int[] { requestTime, simulation.Params.SimulationTimeWindow[1] };                                                                                           //the customer pickup time will be between the current request time and the end of simulation time
                var customer         = CustomerFactory.Instance().CreateRandomCustomer(simulation.Context.Stops, excludedStops, requestTime, pickupTimeWindow, false, simulation.Params.VehicleSpeed); //Generates a random static customer
                customersToBeServed.Add(customer);
            }
            var indexManager = new DataModelIndexManager(dataModelVehicles, customersToBeServed, startDepotsArrivalTime);

            var routingDataModel = new RoutingDataModel(indexManager, simulation.Params.MaximumRelativeCustomerRideTime, simulation.Params.MaximumAllowedDeliveryDelay);
            var solver           = new RoutingSolver(routingDataModel, allowDropNodes);
            RoutingSearchParameters searchParameters = operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.LocalSearchMetaheuristic = LocalSearchMetaheuristic.Types.Value.Automatic;
            searchParameters.SolutionLimit            = 1; //until it finds 1 solution
            var solution = solver.TryGetSolution(searchParameters);

            if (solution == null)
            {
                goto GenerateNewDataModelLabel;
            }
            return(routingDataModel);
        }
Beispiel #13
0
        private RoutingSearchParameters GetDefaultSearchParameters()
        {
            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy =
                FirstSolutionStrategy.Types.Value.ParallelCheapestInsertion;

            return(searchParameters);
        }
Beispiel #14
0
        /// <summary>
        ///   Print the solution.
        /// </summary>
        static void VRP_LongestSingleRoute()
        {
            Console.WriteLine("VRP_LongestSingleRoute!");
            // Instantiate the data problem.
            ORDataModel data = new ORDataModel();

            // public RoutingIndexManager(int num_nodes, int num_vehicles, int depot);
            //public RoutingIndexManager(int num_nodes, int num_vehicles, int[] starts, int[] ends);

            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(
                data.DistanceMatrix.GetLength(0),
                data.VehicleNumber,
                data.Depot);


            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);

            // Create and register a transit callback.
            int transitCallbackIndex = routing.RegisterTransitCallback(
                (long fromIndex, long toIndex) => {
                // Convert from routing variable Index to distance matrix NodeIndex.
                var fromNode = manager.IndexToNode(fromIndex);
                var toNode   = manager.IndexToNode(toIndex);
                return(data.DistanceMatrix[fromNode, toNode]);
            }
                );

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

            // Add Distance constraint.
            routing.AddDimension(transitCallbackIndex, 0, 3000,
                                 true, // start cumul to zero
                                 "Distance");
            RoutingDimension distanceDimension = routing.GetMutableDimension("Distance");

            distanceDimension.SetGlobalSpanCostCoefficient(100);

            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy =
                FirstSolutionStrategy.Types.Value.PathCheapestArc;

            // Solve the problem.
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // Print solution on console.
            PrintSolutionPathCheapest(data, routing, manager, solution);
        }
Beispiel #15
0
        private void Distancia()
        {
            long routeDistance = 0;
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;

            DataModel           data    = new DataModel(Distancias, 1, 0);
            RoutingIndexManager manager = new RoutingIndexManager(
                data.DistanceMatrix.GetLength(0),
                data.VehicleNumber,
                data.Depot);
            RoutingModel routing = new RoutingModel(manager);

            int transitCallbackIndex = routing.RegisterTransitCallback(
                (long fromIndex, long toIndex) => {
                // Convert from routing variable Index to distance matrix NodeIndex.
                var fromNode = manager.IndexToNode(fromIndex);
                var toNode   = manager.IndexToNode(toIndex);
                return(data.DistanceMatrix[fromNode, toNode]);
            });

            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

            Assignment solution = routing.SolveWithParameters(searchParameters);


            var    index   = routing.Start(0);
            int    indice  = 1;
            string CadenaR = "";
            string c       = "";

            while (routing.IsEnd(index) == false)
            {
                //Recorrido[indice] = manager.IndexToNode((int)index);
                c       = manager.IndexToNode((int)index).ToString();
                CadenaR = CadenaR + " - " + c;
                var previousIndex = index;
                index          = solution.Value(routing.NextVar(index));
                routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0);
                indice++;
            }
            //Recorrido[indice] = manager.IndexToNode((int)index);
            c       = manager.IndexToNode((int)index).ToString();
            CadenaR = CadenaR + " - " + c;

            Km = routeDistance;
            CadenaRecorrido = CadenaR;
        }
Beispiel #16
0
        public override Assignment GetSolution()
        {
            Assignment solution = null;

            if (AlgorithmValue is FirstSolutionStrategy.Types.Value firstSolutionAlgorithm)
            {
                RoutingSearchParameters searchParameters = operations_research_constraint_solver.DefaultRoutingSearchParameters();
                searchParameters.FirstSolutionStrategy = firstSolutionAlgorithm;
                //searchParameters.TimeLimit = new Duration { Seconds = SearchTimeLimitInSeconds };
                solution = Solver.TryGetSolution(searchParameters);
            }
            else
            {
                throw new ArgumentException("algorithm value is invalid");
            }
            return(solution);
        }
Beispiel #17
0
        public void InitializeFlexibleSimulation(bool allowDropNodes)
        {
            var dataModel = DataModelFactory.Instance().CreateInitialSimulationDataModel(allowDropNodes, this);

            if (dataModel != null)
            {
                RoutingSolver routingSolver = new RoutingSolver(dataModel, false);
                var           printableList = dataModel.GetSettingsPrintableList();
                foreach (var tobePrinted in printableList)
                {
                    Console.WriteLine(tobePrinted);
                }
                //dataModel.PrintDataStructures();
                Assignment timeWindowSolution            = null;
                RoutingSearchParameters searchParameters =
                    operations_research_constraint_solver.DefaultRoutingSearchParameters();
                searchParameters.FirstSolutionStrategy =
                    FirstSolutionStrategy.Types.Value.ParallelCheapestInsertion;
                searchParameters.LocalSearchMetaheuristic = LocalSearchMetaheuristic.Types.Value.TabuSearch;
                searchParameters.TimeLimit = new Duration {
                    Seconds = 20
                };
                timeWindowSolution = routingSolver.TryGetSolution(searchParameters);


                RoutingSolutionObject routingSolutionObject = null;
                if (timeWindowSolution != null)
                {
                    routingSolver.PrintSolution(timeWindowSolution);

                    routingSolutionObject = routingSolver.GetSolutionObject(timeWindowSolution);
                    for (int j = 0; j < routingSolutionObject.VehicleNumber; j++) //Initializes the flexible trips
                    {
                        var solutionVehicle          = routingSolutionObject.IndexToVehicle(j);
                        var solutionVehicleStops     = routingSolutionObject.GetVehicleStops(solutionVehicle);
                        var solutionTimeWindows      = routingSolutionObject.GetVehicleTimeWindows(solutionVehicle);
                        var solutionVehicleCustomers = routingSolutionObject.GetVehicleCustomers(solutionVehicle);
                        InitializeVehicleFlexibleRoute(solutionVehicle, solutionVehicleStops, solutionVehicleCustomers, solutionTimeWindows);
                    }
                }
            }
            Simulate();
        }
Beispiel #18
0
        public FileOutput Solve(FileInput input, RoutingSearchParameters searchParameters)
        {
            WorkData data = converter.ToWorkData(input);

            RoutingIndexManager manager = new RoutingIndexManager(data.DistanceMatrix.GetLength(0), data.VehiclesAmount, data.Starts, data.Ends);
            RoutingModel        routing = new RoutingModel(manager);

            AddCapacityConstrains(data, manager, routing);
            AddTimeWindowConstrains(data, manager, routing);
            AddPenaltiesAndDroppingVisits(data, manager, routing);

            Assignment solution = routing.SolveWithParameters(searchParameters);

            if (solution == null)
            {
                return(null);
            }
            return(converter.ConvertToFileOutput(input, manager, routing, solution));
        }
        /// <summary>
        ///   Print the solution.
        /// </summary>


        public static int[] Solve()
        {
            int[] result = new int[200];
            int   cnt    = 0;
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(Ranks, VehicleNumber, Depot);

            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);

            int transitCallbackIndex = routing.RegisterTransitCallback(
                (long fromIndex, long toIndex) => {
                // Convert from routing variable Index to distance matrix NodeIndex.
                var fromNode = manager.IndexToNode(fromIndex);
                var toNode   = manager.IndexToNode(toIndex);
                return((long)distMat[fromNode, toNode]);
            }
                );

            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

            // Setting first solution heuristic.
            RoutingSearchParameters searchParameters =
                operations_research_constraint_solver.DefaultRoutingSearchParameters();

            searchParameters.FirstSolutionStrategy =
                FirstSolutionStrategy.Types.Value.PathCheapestArc;

            // Solve the problem.
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // Inspect solution.
            var index = routing.Start(0);

            while (routing.IsEnd(index) == false)
            {
                result[cnt++] = Convert.ToInt32(index);
                var previousIndex = index;
                index = solution.Value(routing.NextVar(index));
            }
            return(result);
        }
Beispiel #20
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);
        }
Beispiel #21
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)))
                             );
            }
        }
Beispiel #22
0
        public static int[] Run(long[] DistanceMatrix, int num, int VehicleNumber, int Depot, RoutingSearchParameters searchParameters)
        {
            // Instantiate the data problem.
            // Create Routing Index Manager
            RoutingIndexManager manager = new RoutingIndexManager(
                num,
                VehicleNumber,
                Depot);

            // Create Routing Model.
            RoutingModel routing = new RoutingModel(manager);

            int transitCallbackIndex = routing.RegisterTransitCallback(
                (long fromIndex, long toIndex) => {
                // Convert from routing variable Index to distance matrix NodeIndex.
                var fromNode = manager.IndexToNode(fromIndex);
                var toNode   = manager.IndexToNode(toIndex);
                return(DistanceMatrix[fromNode * num + toNode]);
            }
                );

            // Define cost of each arc.
            routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

            // Solve the problem.
            Assignment solution = routing.SolveWithParameters(searchParameters);

            // Print solution on console.
            return(GetOrder(routing, manager, solution));
        }
Beispiel #23
0
    public static void Main(String[] args)
    {
        // Instantiate the data problem.
        DataModel data = new DataModel();

        // Create Routing Index Manager
        RoutingIndexManager manager = new RoutingIndexManager(
            data.DistanceMatrix.GetLength(0),
            data.VehicleNumber,
            data.Depot);

        // Create Routing Model.
        RoutingModel routing = new RoutingModel(manager);

        // Create and register a transit callback.
        int transitCallbackIndex = routing.RegisterTransitCallback(
            (long fromIndex, long toIndex) =>
        {
            // Convert from routing variable Index to distance matrix NodeIndex.
            var fromNode = manager.IndexToNode(fromIndex);
            var toNode   = manager.IndexToNode(toIndex);
            return(data.DistanceMatrix[fromNode, toNode]);
        }
            );

        // Define cost of each arc.
        routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

        // Add Distance constraint.
        routing.AddDimension(
            transitCallbackIndex,
            0,
            3000,
            true, // start cumul to zero
            "Distance");
        RoutingDimension distanceDimension = routing.GetMutableDimension("Distance");

        distanceDimension.SetGlobalSpanCostCoefficient(100);

        // Define Transportation Requests.
        Solver solver = routing.solver();

        for (int i = 0; i < data.PickupsDeliveries.GetLength(0); i++)
        {
            long pickupIndex   = manager.NodeToIndex(data.PickupsDeliveries[i][0]);
            long deliveryIndex = manager.NodeToIndex(data.PickupsDeliveries[i][1]);
            routing.AddPickupAndDelivery(pickupIndex, deliveryIndex);
            solver.Add(solver.MakeEquality(
                           routing.VehicleVar(pickupIndex),
                           routing.VehicleVar(deliveryIndex)));
            solver.Add(solver.MakeLessOrEqual(
                           distanceDimension.CumulVar(pickupIndex),
                           distanceDimension.CumulVar(deliveryIndex)));
        }

        // Setting first solution heuristic.
        RoutingSearchParameters searchParameters =
            operations_research_constraint_solver.DefaultRoutingSearchParameters();

        searchParameters.FirstSolutionStrategy =
            FirstSolutionStrategy.Types.Value.PathCheapestArc;

        // Solve the problem.
        Assignment solution = routing.SolveWithParameters(searchParameters);

        // Print solution on console.
        PrintSolution(data, routing, manager, solution);
    }
Beispiel #24
0
    /// <summary>
    ///   Solves the current routing problem.
    /// </summary>
    private void Solve(int number_of_orders, int number_of_vehicles)
    {
        Console.WriteLine("Creating model with " + number_of_orders + " orders and " + number_of_vehicles +
                          " vehicles.");
        // Finalizing model
        int number_of_locations = locations_.Length;

        RoutingIndexManager manager =
            new RoutingIndexManager(number_of_locations, number_of_vehicles, vehicle_starts_, vehicle_ends_);
        RoutingModel model = new RoutingModel(manager);

        // Setting up dimensions
        const int big_number         = 100000;
        Manhattan manhattan_callback = new Manhattan(manager, locations_, 1);

        model.AddDimension(model.RegisterTransitCallback(manhattan_callback.Call), big_number, big_number, false,
                           "time");
        RoutingDimension time_dimension = model.GetDimensionOrDie("time");

        Demand demand_callback = new Demand(manager, order_demands_);

        model.AddDimension(model.RegisterUnaryTransitCallback(demand_callback.Call), 0, vehicle_capacity_, true,
                           "capacity");
        RoutingDimension capacity_dimension = model.GetDimensionOrDie("capacity");

        // Setting up vehicles
        Manhattan[] cost_callbacks = new Manhattan[number_of_vehicles];
        for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle)
        {
            int       cost_coefficient        = vehicle_cost_coefficients_[vehicle];
            Manhattan manhattan_cost_callback = new Manhattan(manager, locations_, cost_coefficient);
            cost_callbacks[vehicle] = manhattan_cost_callback;
            int manhattan_cost_index = model.RegisterTransitCallback(manhattan_cost_callback.Call);
            model.SetArcCostEvaluatorOfVehicle(manhattan_cost_index, vehicle);
            time_dimension.CumulVar(model.End(vehicle)).SetMax(vehicle_end_time_[vehicle]);
        }

        // Setting up orders
        for (int order = 0; order < number_of_orders; ++order)
        {
            time_dimension.CumulVar(order).SetRange(order_time_windows_[order].start_, order_time_windows_[order].end_);
            long[] orders = { manager.NodeToIndex(order) };
            model.AddDisjunction(orders, order_penalties_[order]);
        }

        // Solving
        RoutingSearchParameters search_parameters =
            operations_research_constraint_solver.DefaultRoutingSearchParameters();

        search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.AllUnperformed;

        Console.WriteLine("Search...");
        Assignment solution = model.SolveWithParameters(search_parameters);

        if (solution != null)
        {
            String output = "Total cost: " + solution.ObjectiveValue() + "\n";
            // Dropped orders
            String dropped = "";
            for (int order = 0; order < number_of_orders; ++order)
            {
                if (solution.Value(model.NextVar(order)) == order)
                {
                    dropped += " " + order;
                }
            }
            if (dropped.Length > 0)
            {
                output += "Dropped orders:" + dropped + "\n";
            }
            // Routes
            for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle)
            {
                String route = "Vehicle " + vehicle + ": ";
                long   order = model.Start(vehicle);
                if (model.IsEnd(solution.Value(model.NextVar(order))))
                {
                    route += "Empty";
                }
                else
                {
                    for (; !model.IsEnd(order); order = solution.Value(model.NextVar(order)))
                    {
                        IntVar local_load = capacity_dimension.CumulVar(order);
                        IntVar local_time = time_dimension.CumulVar(order);
                        route += order + " Load(" + solution.Value(local_load) + ") " + "Time(" +
                                 solution.Min(local_time) + ", " + solution.Max(local_time) + ") -> ";
                    }
                    IntVar load = capacity_dimension.CumulVar(order);
                    IntVar time = time_dimension.CumulVar(order);
                    route += order + " Load(" + solution.Value(load) + ") " + "Time(" + solution.Min(time) + ", " +
                             solution.Max(time) + ")";
                }
                output += route + "\n";
            }
            Console.WriteLine(output);
        }
    }
Beispiel #25
0
        public IList <string> GetShortestOptimizedRoute([FromBody] GeographicViewModel geographicVM)
        {
            // double[] latitude = { 13.121329, 13.065150, 13.024346, 13.027691, 12.913887, 12.915754, 12.962431, 12.890461, 12.907220, 12.954234, 13.026996, 13.041044, 13.001573 };
            //double[] longitude = { 80.029049, 80.128613, 79.909573, 80.259762, 80.253067, 80.192041, 80.253839, 80.097198, 80.142088, 80.188437, 80.107756, 80.234957, 80.257616 };
            string[] city_names = { "Thirunindravur", "Thiruverkadu", "Senkadu", "Milapore", "VGP Golden", "Medavakkam", "Palavakkam", "Vandalur", "Selaiyur", "Kelkattalai", "Mangadu", "TNagar", "Adyar" };

            double[] latitudeandLongitude;
            var      sCoordlatitudeandLongitude = new List <string>();
            var      finallatitudeandLongitude  = new List <string>();

            distanceArray = new double[geographicVM.Latitude.Length, geographicVM.Longitude.Length];
            var k = 0;

            for (int i = 0; i < geographicVM.Latitude.Length; i++)
            {
                for (int j = 0; j < geographicVM.Longitude.Length; j++)
                {
                    var sCoord = new GeoCoordinate(geographicVM.Latitude[i], geographicVM.Longitude[i]);
                    var eCoord = new GeoCoordinate(geographicVM.Latitude[j], geographicVM.Longitude[j]);
                    if (i == j)
                    {
                        if (i % 2 == 0)
                        {
                            sCoordlatitudeandLongitude.Add(sCoord.ToString() + ",D" + "," + k++);
                        }
                        else
                        {
                            sCoordlatitudeandLongitude.Add(sCoord.ToString() + ",P" + "," + k);
                        }
                    }

                    var text = sCoord.GetDistanceTo(eCoord) / 1609.344;
                    distanceArray[i, j] = Math.Round(text);
                }
            }
            double[,] costs = distanceArray;

            int          num_locations = city_names.Length;
            RoutingModel routingModel  = new RoutingModel(geographicVM.Latitude.Length, 1, 0);

            Solver solver = routingModel.solver();


            string rank_name = "rank";

            routingModel.AddConstantDimension(1, geographicVM.Latitude.Length, true, rank_name);
            var rank_dimension = routingModel.GetDimensionOrDie(rank_name);

            //Constraint MinneapolisBeforeNewYork = solver.MakeLess(routingModel.CumulVar(highPriorityVarIndex, rank_name), routingModel.CumulVar(lowPriorityVarIndex, rank_name));

            /* Later needs to be worked on the Constraint for the  Multiple Pickups before Delivery */
            //Constraint test = routingModel.CumulVar(3, rank_name) < routingModel.CumulVar(13, rank_name);
            //solver.Add(test);


            RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters();

            search_parameters.FirstSolutionStrategy =
                FirstSolutionStrategy.Types.Value.LocalCheapestArc;
            NodeEvaluator2 cost_between_nodes = new Cost(costs);

            routingModel.SetArcCostEvaluatorOfAllVehicles(cost_between_nodes); //oder SetVehicleCost wenn Fahrzeuge unterschiedliche Kostenmatrix haben

            StringBuilder route = new StringBuilder();

            Assignment assignment = routingModel.SolveWithParameters(search_parameters);

            if (assignment != null)
            {
                Console.WriteLine("Total distance: " + assignment.ObjectiveValue().ToString() + "miles");

                long index = routingModel.Start(0); //vehicle 0

                route.Append("Route: ");

                do
                {
                    //route.Append(city_names[routingModel.IndexToNode(index)] + " -> ");
                    finallatitudeandLongitude.Add(sCoordlatitudeandLongitude[routingModel.IndexToNode(index)]);
                    index = assignment.Value(routingModel.NextVar(index));
                }while (!routingModel.IsEnd(index));
                // route.Append(city_names[routingModel.IndexToNode(index)]);
                finallatitudeandLongitude.Add(sCoordlatitudeandLongitude[routingModel.IndexToNode(index)]);
            }


            return(finallatitudeandLongitude);
        }
Beispiel #26
0
        static void Main(string[] args)
        {
            List <string> Addresses = new List <string>()
            {
                "New York", "Los Angeles", "Chicago", "Minneapolis",
                "Denver", "Dallas", "Seattle", "Boston", "San Francisco", "St.Louis",
                "Houston", "Phoenix", "Salt Lake City"
            };

            APIGoogle.Register("AIzaSyCkYDMEPLWCFvq3Oi-LJyEsMuh_06Fk62g");

            List <LatLng> listLatLng = new List <LatLng>();

            foreach (string item in Addresses)
            {
                listLatLng.Add(APIGoogle.GetLatLng(item));
            }

            DirectionRequest directionRequest = new DirectionRequest();
            DirectionService directionService = new DirectionService();

            long[,] CityDistanceMatrix = new long[Addresses.Count, Addresses.Count];

            for (int i = 0; i < Addresses.Count; i++)
            {
                for (int j = 0; j < Addresses.Count; j++)
                {
                    directionRequest.Origin = Addresses[i];
                    directionRequest.Sensor = false;
                    {
                        directionRequest.Destination = Addresses[j];
                        var ttt = directionService.GetResponse(directionRequest);
                        CityDistanceMatrix[i, j] = directionService.GetResponse(directionRequest).Routes[0].Legs[0].Distance.Value / 1000;
                    };
                }
            }

            int NumRoutes = 1;    // The number of routes, which is 1 in the TSP.
                                  // Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
            int Depot   = 0;
            int TspSize = Addresses.Count;

            if (TspSize > 0)
            {
                RoutingModel routing = new RoutingModel(TspSize, NumRoutes, Depot);

                RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters();

                CityDistance dist_between_nodes = new CityDistance(CityDistanceMatrix, Addresses);
                routing.SetArcCostEvaluatorOfAllVehicles(dist_between_nodes);
                //routing.SetCost(dist_between_nodes);

                Demand demands_at_locations = new Demand(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });


                search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
                Assignment solution = routing.SolveWithParameters(search_parameters);

                Console.WriteLine("Status = {0}", routing.Status());
                if (solution != null)
                {
                    // Solution cost.
                    Console.WriteLine("Suma [km]= {0}", solution.ObjectiveValue() / 1000);
                    // Inspect solution.
                    // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1
                    int route_number = 0;
                    for (long node = routing.Start(route_number);
                         !routing.IsEnd(node);
                         node = solution.Value(routing.NextVar(node)))
                    {
                        Console.Write("{0} \n", Addresses[(int)node]);
                    }
                    Console.WriteLine(Addresses[0]);
                }
            }
            Console.ReadKey();
        }
    /// <summary>
    ///   Solves the current routing problem.
    /// </summary>
    static void Solve()
    {
        // Instantiate the data problem.
        // [START data]
        const int num_location = 5;
        const int num_vehicles = 1;
        const int depot        = 0;
        // [END data]

        // Create Routing Index Manager
        // [START index_manager]
        RoutingIndexManager manager = new RoutingIndexManager(num_location, num_vehicles, depot);
        // [END index_manager]

        // Create Routing Model.
        // [START routing_model]
        RoutingModel routing = new RoutingModel(manager);

        // [END routing_model]

        // Define cost of each arc.
        // [START arc_cost]
        routing.SetArcCostEvaluatorOfAllVehicles(
            routing.RegisterTransitCallback(
                (long FromIndex, long ToIndex) => {
            return(1L);
        }
                ));
        // [END arc_cost]

        // Setting first solution heuristic.
        // [START parameters]
        RoutingSearchParameters searchParameters =
            operations_research_constraint_solver.DefaultRoutingSearchParameters();

        searchParameters.FirstSolutionStrategy =
            FirstSolutionStrategy.Types.Value.PathCheapestArc;
        // [END parameters]

        // Solve the problem.
        // [START solve]
        Assignment solution = routing.SolveWithParameters(searchParameters);

        // [END solve]

        // Print solution on console.
        // [START print_solution]
        Console.WriteLine("Objective: {0}", solution.ObjectiveValue());
        // Inspect solution.
        long index = routing.Start(0);

        Console.WriteLine("Route for Vehicle 0:");
        long route_distance = 0;

        while (routing.IsEnd(index) == false)
        {
            Console.Write("{0} -> ", manager.IndexToNode((int)index));
            long previousIndex = index;
            index           = solution.Value(routing.NextVar(index));
            route_distance += routing.GetArcCostForVehicle(previousIndex, index, 0);
        }
        Console.WriteLine("{0}", manager.IndexToNode(index));
        Console.WriteLine("Distance of the route: {0}m", route_distance);
        // [END print_solution]
    }
Beispiel #28
0
    /// <summary>
    ///   Solves the current routing problem.
    /// </summary>
    private void Solve(int number_of_orders, int number_of_vehicles)
    {
        Console.WriteLine("Creating model with " + number_of_orders +
                          " orders and " + number_of_vehicles + " vehicles.");
        // Finalizing model
        int number_of_locations = locations_.Length;

        RoutingModel model =
            new RoutingModel(number_of_locations, number_of_vehicles,
                             vehicle_starts_, vehicle_ends_);

        // Setting up dimensions
        const int      big_number         = 100000;
        NodeEvaluator2 manhattan_callback = new Manhattan(locations_, 1);

        model.AddDimension(
            manhattan_callback, big_number, big_number, false, "time");
        NodeEvaluator2 demand_callback = new Demand(order_demands_);

        model.AddDimension(demand_callback, 0, vehicle_capacity_, true, "capacity");

        // Setting up vehicles
        NodeEvaluator2[] cost_callbacks = new NodeEvaluator2[number_of_vehicles];
        for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle)
        {
            int            cost_coefficient        = vehicle_cost_coefficients_[vehicle];
            NodeEvaluator2 manhattan_cost_callback =
                new Manhattan(locations_, cost_coefficient);
            cost_callbacks[vehicle] = manhattan_cost_callback;
            model.SetVehicleCost(vehicle, manhattan_cost_callback);
            model.CumulVar(model.End(vehicle), "time").SetMax(
                vehicle_end_time_[vehicle]);
        }

        // Setting up orders
        for (int order = 0; order < number_of_orders; ++order)
        {
            model.CumulVar(order, "time").SetRange(order_time_windows_[order].start_,
                                                   order_time_windows_[order].end_);
            int[] orders = { order };
            model.AddDisjunction(orders, order_penalties_[order]);
        }

        // Solving
        RoutingSearchParameters search_parameters =
            RoutingModel.DefaultSearchParameters();

        search_parameters.FirstSolutionStrategy =
            FirstSolutionStrategy.Types.Value.AllUnperformed;

        Console.WriteLine("Search");
        Assignment solution = model.SolveWithParameters(search_parameters);

        //protect callbacks from the GC
        GC.KeepAlive(manhattan_callback);
        GC.KeepAlive(demand_callback);
        for (int cost_callback_index = 0; cost_callback_index < cost_callbacks.Length; cost_callback_index++)
        {
            GC.KeepAlive(cost_callbacks[cost_callback_index]);
        }

        if (solution != null)
        {
            String output = "Total cost: " + solution.ObjectiveValue() + "\n";
            // Dropped orders
            String dropped = "";
            for (int order = 0; order < number_of_orders; ++order)
            {
                if (solution.Value(model.NextVar(order)) == order)
                {
                    dropped += " " + order;
                }
            }
            if (dropped.Length > 0)
            {
                output += "Dropped orders:" + dropped + "\n";
            }
            // Routes
            for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle)
            {
                String route = "Vehicle " + vehicle + ": ";
                long   order = model.Start(vehicle);
                if (model.IsEnd(solution.Value(model.NextVar(order))))
                {
                    route += "Empty";
                }
                else
                {
                    for (;
                         !model.IsEnd(order);
                         order = solution.Value(model.NextVar(order)))
                    {
                        IntVar local_load = model.CumulVar(order, "capacity");
                        IntVar local_time = model.CumulVar(order, "time");
                        route += order + " Load(" + solution.Value(local_load) + ") " +
                                 "Time(" + solution.Min(local_time) + ", " +
                                 solution.Max(local_time) + ") -> ";
                    }
                    IntVar load = model.CumulVar(order, "capacity");
                    IntVar time = model.CumulVar(order, "time");
                    route += order + " Load(" + solution.Value(load) + ") " +
                             "Time(" + solution.Min(time) + ", " + solution.Max(time) + ")";
                }
                output += route + "\n";
            }
            Console.WriteLine(output);
        }
    }
 /// <summary>
 /// Constructs a new Event Arguments instance.
 /// </summary>
 /// <param name="searchParams"></param>
 internal RoutingSearchParametersEventArgs(RoutingSearchParameters searchParams)
 {
     this.Parameters = searchParams;
 }
Beispiel #30
0
  /// <summary>
  ///   Solves the current routing problem.
  /// </summary>
  private void Solve(int number_of_orders, int number_of_vehicles) {
    Console.WriteLine("Creating model with " + number_of_orders +
                      " orders and " + number_of_vehicles + " vehicles.");
    // Finalizing model
    int number_of_locations = locations_.Length;

    RoutingModel model =
        new RoutingModel(number_of_locations, number_of_vehicles,
                         vehicle_starts_, vehicle_ends_);

    // Setting up dimensions
    const int big_number = 100000;
    NodeEvaluator2 manhattan_callback = new Manhattan(locations_, 1);
    model.AddDimension(
        manhattan_callback, big_number, big_number, false, "time");
    NodeEvaluator2 demand_callback = new Demand(order_demands_);
    model.AddDimension(demand_callback, 0, vehicle_capacity_, true, "capacity");

    // Setting up vehicles
    for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) {
      int cost_coefficient = vehicle_cost_coefficients_[vehicle];
      NodeEvaluator2 manhattan_cost_callback =
          new Manhattan(locations_, cost_coefficient);
      model.SetVehicleCost(vehicle, manhattan_cost_callback);
      model.CumulVar(model.End(vehicle), "time").SetMax(
          vehicle_end_time_[vehicle]);
    }

    // Setting up orders
    for (int order = 0; order < number_of_orders; ++order) {
      model.CumulVar(order, "time").SetRange(order_time_windows_[order].start_,
                                             order_time_windows_[order].end_);
      int[] orders = {order};
      model.AddDisjunction(orders, order_penalties_[order]);
    }

    // Solving
    RoutingSearchParameters parameters = new RoutingSearchParameters();
    parameters.no_lns = true;
    parameters.first_solution = "AllUnperformed";
    parameters.trace = true;

    Console.WriteLine("Search");
    Assignment solution = model.SolveWithParameters(parameters, null);

    if (solution != null) {
      String output = "Total cost: " + solution.ObjectiveValue() + "\n";
      // Dropped orders
      String dropped = "";
      for (int order = 0; order < number_of_orders; ++order) {
        if (solution.Value(model.NextVar(order)) == order) {
          dropped += " " + order;
        }
      }
      if (dropped.Length > 0) {
        output += "Dropped orders:" + dropped + "\n";
      }
      // Routes
      for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) {
        String route = "Vehicle " + vehicle + ": ";
        long order = model.Start(vehicle);
        if (model.IsEnd(solution.Value(model.NextVar(order)))) {
          route += "Empty";
        } else {
          for (;
               !model.IsEnd(order);
               order = solution.Value(model.NextVar(order))) {
            IntVar local_load = model.CumulVar(order, "capacity");
            IntVar local_time = model.CumulVar(order, "time");
            route += order + " Load(" + solution.Value(local_load) + ") " +
                "Time(" + solution.Min(local_time) + ", " +
                solution.Max(local_time) + ") -> ";
          }
          IntVar load = model.CumulVar(order, "capacity");
          IntVar time = model.CumulVar(order, "time");
          route += order + " Load(" + solution.Value(load) + ") " +
              "Time(" + solution.Min(time) + ", " + solution.Max(time) + ")";
        }
        output += route + "\n";
      }
      Console.WriteLine(output);
    }
  }
        public override void Handle(Event evt)
        {  //INSERTION OF EVENTS FOR THE NEWLY GENERATED ROUTE ( after a dynamic request has been accepted)
            if (evt.Category == 4 && evt is CustomerRequestEvent customerRequestEvent)
            {
                Log(evt);
                evt.AlreadyHandled = true;
                _consoleLogger.Log("New Customer (dynamic) request:" + customerRequestEvent.Customer + " - " + customerRequestEvent.Customer.PickupDelivery[0] + " -> " + customerRequestEvent.Customer.PickupDelivery[1] + ", TimeWindows: {" + customerRequestEvent.Customer.DesiredTimeWindow[0] + "," + customerRequestEvent.Customer.DesiredTimeWindow[1] + "} at " + TimeSpan.FromSeconds(evt.Time).ToString());
                //Check if request can be served, if so the
                Simulation.Stats.TotalDynamicRequests++;
                var newCustomer = customerRequestEvent.Customer;
                RoutingSolutionObject solutionObject = null;
                if (Simulation.Context.VehicleFleet.FindAll(v => v.FlexibleRouting).Count > 0 && newCustomer != null)
                {
                    var dataModel = DataModelFactory.Instance().CreateCurrentSimulationDataModel(Simulation, newCustomer, evt.Time);
                    var solver    = new RoutingSolver(dataModel, false);
                    RoutingSearchParameters searchParameters =
                        operations_research_constraint_solver.DefaultRoutingSearchParameters();
                    searchParameters.FirstSolutionStrategy =
                        FirstSolutionStrategy.Types.Value.ParallelCheapestInsertion;
                    searchParameters.LocalSearchMetaheuristic = LocalSearchMetaheuristic.Types.Value.TabuSearch;
                    searchParameters.TimeLimit = new Duration {
                        Seconds = 5
                    };                                                        //change
                    _consoleLogger.Log("Generating new Routing Solution...");
                    var solution = solver.TryGetSolution(searchParameters);
                    if (solution != null)
                    {
                        //solver.PrintSolution(solution);
                        Simulation.Stats.TotalServedDynamicRequests++;
                        _consoleLogger.Log(newCustomer.ToString() + " request was inserted into a vehicle route at " + TimeSpan.FromSeconds(customerRequestEvent.Time).ToString());

                        solutionObject = solver.GetSolutionObject(solution);
                    }
                    else
                    {
                        _consoleLogger.Log(newCustomer.ToString() + " will not be able to be served by any of the available vehicles at " + TimeSpan.FromSeconds(customerRequestEvent.Time).ToString());
                    }
                }

                if (!Simulation.Context.DynamicCustomers.Contains(newCustomer))
                {
                    Simulation.Context.DynamicCustomers.Add(newCustomer);
                }

                if (solutionObject != null)
                {
                    //solutionObject.MetricsContainer.PrintMetrics();
                    var vehicleFlexibleRouting = Simulation.Context.VehicleFleet.FindAll(v => v.FlexibleRouting);
                    foreach (var vehicle in vehicleFlexibleRouting)
                    {
                        var solutionRoute       = solutionObject.GetVehicleStops(vehicle);
                        var solutionTimeWindows = solutionObject.GetVehicleTimeWindows(vehicle);
                        var solutionCustomers   = solutionObject.GetVehicleCustomers(vehicle);
                        if (solutionRoute.Count >= 2 && solutionRoute[0] != solutionRoute[1])//check if current vehicle route is valid
                        {
                            if (vehicle.VisitedStops.Count > 1 && vehicle.CurrentStop == Simulation.Context.Depot)
                            {
                                _consoleLogger.Log("Vehicle " + vehicle.Id + " already performed a route and is currently idle at depot.");//debug
                            }
                            if (vehicle.TripIterator?.Current != null)
                            {
                                var           currentStopIndex   = vehicle.TripIterator.Current.StopsIterator.CurrentIndex;
                                var           customers          = solutionObject.GetVehicleCustomers(vehicle); //contains all customers (already inside and not yet in vehicle)
                                List <Stop>   visitedStops       = new List <Stop>(vehicle.VisitedStops);
                                List <long[]> visitedTimeWindows = new List <long[]>(vehicle.StopsTimeWindows);


                                if (currentStopIndex < vehicle.VisitedStops.Count)
                                {
                                    visitedStops.RemoveAt(currentStopIndex);       //remove current stop from the visitedStops list
                                    visitedTimeWindows.RemoveAt(currentStopIndex); //remove current timeWindow from the visitedTimeWindows list
                                }

                                //inserts the already visited stops at the beginning of the solutionRoute list
                                for (int e = visitedStops.Count - 1; e >= 0; e--)
                                {
                                    solutionRoute.Insert(0, visitedStops[e]);
                                    solutionTimeWindows.Insert(0, visitedTimeWindows[e]);
                                }

                                vehicle.TripIterator.Current.AssignStops(solutionRoute, solutionTimeWindows, currentStopIndex);
                                vehicle.TripIterator.Current.ExpectedCustomers = customers.FindAll(c => !c.IsInVehicle);                                                                                                                                                                                  //the expected customers for the current vehicle are the ones that are not in that vehicle

                                var vehicleEvents = Simulation.Events.FindAll(e => (e is VehicleStopEvent vse && vse.Vehicle == vehicle && e.Time >= evt.Time) || (e is CustomerVehicleEvent cve && cve.Vehicle == vehicle && e.Time >= evt.Time)).OrderBy(e => e.Time).ThenBy(e => e.Category).ToList(); //gets all next vehicle depart or arrive events
                                if (vehicleEvents.Count > 0)
                                {
                                    foreach (var vEvent in vehicleEvents)
                                    {
                                        if (vEvent is VehicleStopEvent vehicleStopArriveEvent && vEvent.Category == 0
                                            ) //vehicle arrive stop event
                                        {
                                            //_consoleLogger.Log(vehicleStopArriveEvent.GetTraceMessage());
                                        }

                                        if (vEvent is VehicleStopEvent vehicleStopDepartEvent && vEvent.Category == 1
                                            ) //vehicle depart stop event
                                        {
                                            var departTime = vEvent.Time;

                                            if (vehicleStopDepartEvent.Stop == vehicle.CurrentStop)
                                            {
                                                departTime =
                                                    (int)vehicle.TripIterator.Current.ScheduledTimeWindows[
                                                        vehicle.TripIterator.Current.StopsIterator.CurrentIndex][1] +
                                                    2; //recalculates new event depart time;


                                                vEvent.Time = departTime;
                                            }

                                            var allEnterLeaveEventsForCurrentVehicle = Simulation.Events
                                                                                       .FindAll(e =>
                                                                                                e.Time >= evt.Time &&
                                                                                                (e is CustomerVehicleEvent cve && cve.Vehicle == vehicle))
                                                                                       .OrderBy(e => e.Time).ThenBy(e => e.Category).ToList();

                                            foreach (var enterOrLeaveEvt in allEnterLeaveEventsForCurrentVehicle)
                                            {
                                                if (enterOrLeaveEvt.Time > departTime
                                                    ) //if the event time is greater than the depart time removes those events, otherwise maintain the enter and leave events for current stop
                                                {
                                                    Simulation.Events.Remove(enterOrLeaveEvt);
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    //if vehicle events is 0 it means the vehicle has no more generated events for it, need to generate a new depart event for the vehicle
                                    vehicle.TripIterator.Current.IsDone = false; //current trip is no longer completed
                                    var currentDepartureTime = (int)solutionTimeWindows[currentStopIndex][1];
                                    if (currentDepartureTime == evt.Time)        //if departure time is the same time as the event being handled adds 1 to its time
                                    {
                                        currentDepartureTime++;
                                    }
                                    var departEvt = Simulation.EventGenerator.GenerateVehicleDepartEvent(vehicle, currentDepartureTime);
                                    Simulation.Events.Add(departEvt);
                                }
                            }
                            else
                            {
                                //trip assignment for vehicles that are have not yet performed a route
                                Simulation.InitializeVehicleFlexibleRoute(vehicle, solutionRoute, solutionCustomers, solutionTimeWindows);
                            }
                        }
                    }
                }
            }
            else
            {
                Successor?.Handle(evt);
            }
        }