/** * @param $rideNodes * @param $emptyRideNodes * @param $drivingPools * @return RideConfiguration[] */ public function buildConfigurations($rideNodes, $drivingPools, $emptyRideNodes) { $this->rideNodes = $rideNodes; $this->drivingPools = $drivingPools; $this->emptyRides = $emptyRideNodes; $workNodes = $this->rideNodes; ConfigurationBuilder::sortNodesByStartMinute($workNodes); $this->adjacenceMatrix = ConfigurationBuilder::buildAdjacenceMatrixFromNodes($workNodes, $emptyRideNodes); //build some different configurations $rideConfigurations = array(); $diversity = count($this->drivingPools); $factor = $diversity * 2; // take for each driving pool another start node, // and then shuffle the whole node array some times // to create another start set to begin with for ($i = 0; $i < $factor; $i++) { $workRideNodes = $workNodes; if ($i > $diversity) { shuffle($workRideNodes); } else { $switch = $workRideNodes[0]; $workRideNodes[0] = $workRideNodes[$i]; $workRideNodes[$i] = $switch; } $rideConfigurations[] = $this->buildGenericLeastDurationConfiguration($workRideNodes); } return $rideConfigurations; }
/** * @param $rideNodes * @param $emptyRideNodes * @param $drivingPools * @return RideConfiguration[] */ public function buildConfigurations($rideNodes, $drivingPools, $emptyRideNodes) { $this->rideNodes = $rideNodes; $this->drivingPools = $drivingPools; $this->emptyRideNodes = $emptyRideNodes; $workNodes = $this->rideNodes; ConfigurationBuilder::sortNodesByStartMinute($workNodes); $this->adjacenceMatrix = ConfigurationBuilder::buildAdjacenceMatrixFromNodes($workNodes, $emptyRideNodes); //Initial Config by LeastDistance Strategy $s = microtime(true); // $initConfig = $this->buildFeasibleConfiguration(); $initConfig = $this->buildFeasibleConfigFromStrategy(new RideStrategyLeastDuration()); $e = microtime(true); $this->logWithParameters('ride.log.ridenodes', array(count($this->rideNodes), round($e - $s, 3))); if ($initConfig) { $configurations = $this->annealConfigurations($initConfig); return $configurations; } return null; }
/** * Production code: * runs routing algorithm to set optimized missions and orders for a shift * * @param Shift $shift * @param boolean $verbose * @return array containing information for user */ public function buildRidePlanForShift(Shift $shift, $verbose = true) { $day = $shift->getDate(); $tr = $this->container->get('translator'); $infos = array(); if ($verbose) { $infos[] = $tr->trans('ride.info.shift') . ' ' . $day->format('d.m.Y') . ', ' . $tr->trans(DateTimeService::getDayOfWeek($day)) . ' ' . $shift->getShiftType()->getName() . '.'; } //set php execution timeout for big calculations ini_set('max_execution_time', 300); $em = $this->container->get('entity_manager'); $em->beginTransaction(); //set STRATEGY for RideOptimization $rideStrategy = new RideStrategyAnnealing(); $rideStrategy->setContainer($this->container); //set Shift $shift->setManuallyEdited(false); // optimization resets manual attribute // set DrivingPools $drivingPools = $shift->getDrivingPoolsAsArray(); if (count($drivingPools) < 1) { $em->rollback(); $infos[] = $tr->trans('ride.error.noPools') . ' ' . $shift->getShiftType()->getName() . '.'; return $infos; } // set DrivingMissions $dispoManagement = $this->container->get('tixi_app.dispomanagement'); $drivingMissions = $dispoManagement->getDrivingMissionsInShift($shift); if (count($drivingMissions) < 1) { $em->rollback(); $infos[] = $tr->trans('ride.error.noMissions') . ' ' . $shift->getShiftType()->getName() . '.'; return $infos; } //clean drivingPools for new optimization foreach ($drivingPools as $pool) { $pool->removeDrivingMissions(); } $configurationBuilder = new ConfigurationBuilder($drivingMissions, $drivingPools, $rideStrategy); $this->logger($tr->trans('ride.log.dayAndShift'), array($day->format('d.m.Y'), $shift->getShiftType()->getName())); //get all empty Rides $emptyRides = $configurationBuilder->buildAllPossibleEmptyRides(); //get routing information from routing machine and fill node objects $s = microtime(true); $routeManagement = $this->container->get('tixi_app.routemanagement'); $emptyRides = $routeManagement->fillRoutesForMultipleRideNodes($emptyRides); $e = microtime(true); $this->logger($tr->trans('ride.log.routingTime'), array(count($emptyRides), round($e - $s, 3))); //create ride configurations with strategy $s = microtime(true); $rideConfigurations = $configurationBuilder->buildConfigurations(); if ($rideConfigurations === null) { $em->rollback(); $infos[] = $tr->trans('ride.error.noConfiguration'); return $infos; } $e = microtime(true); $nodes = count($rideConfigurations); $this->logger($tr->trans('ride.log.buildTime'), array($nodes, round($e - $s, 3))); // get the best feasible configuration $rideConfiguration = $this->getBestConfiguration($rideConfigurations); $configurationAnalyzer = new ConfigurationAnalyzer($rideConfiguration); $configurationAnalyzer->assignMissionsAndVehiclesToPool(); $booked = $rideConfiguration->countNodes(); $overbooked = count($rideConfiguration->getNotFeasibleNodes()); if ($verbose) { $infos[] = $tr->trans('ride.info.nodes') . ' ' . $booked . ', ' . $tr->trans('ride.info.overbooked') . ' ' . $overbooked . '.'; } $this->logger($tr->trans('ride.log.success'), array($nodes, $overbooked)); // if everything worked, return successfully $em->commit(); $em->flush(); return $infos; }
/** * this adds all missions to existing configuration with compare to a time slice window, * no exact routing informations given * @param $rideNodes * @param $drivingPools * @param $emptyRideNodes * @param \Tixi\App\AppBundle\Ride\RideConfiguration $existingConfiguration * @return RideConfiguration */ public function buildConfiguration($rideNodes, $drivingPools, $emptyRideNodes, RideConfiguration $existingConfiguration = null) { $this->rideNodes = $rideNodes; $this->drivingPools = $drivingPools; /**@var $workRideNodes RideNode[] */ $workRideNodes = $this->rideNodes; $addTimePickup = DispositionVariables::ARRIVAL_BEFORE_PICKUP; //remove existing nodes from workSet if ($existingConfiguration) { $rideConfiguration = $existingConfiguration; foreach ($rideConfiguration->getRideNodeLists() as $list) { foreach ($list->getRideNodes() as $key => $node) { unset($workRideNodes[$key]); } } } else { $rideConfiguration = new RideConfiguration($this->drivingPools); } ConfigurationBuilder::sortNodesByStartMinute($workRideNodes); $workRideNodeLists = $rideConfiguration->getRideNodeLists(); //always loop all available pools - but stop when no missions are left so it is //possible to have empty pools (no driver/vehicle needed for these missions) foreach ($drivingPools as $drivingPool) { if (count($workRideNodes) < 1) { break; } if (count($workRideNodeLists) > 0) { $rideNodeList = array_shift($workRideNodeLists); } else { $rideNodeList = new RideNodeList(); $rideConfiguration->addRideNodeList($rideNodeList); } // no existing list with nodes, // so we add rideNodes normally with time constraint if ($rideNodeList->isEmpty()) { $rideNodeList->addRideNode(array_shift($workRideNodes)); $actualNode = $rideNodeList->getActualRideNode(); foreach ($workRideNodes as $nodeKey => $node) { if ($node->startMinute > $actualNode->endMinute + $addTimePickup) { $actualNode = $node; $rideNodeList->addRideNode($node); unset($workRideNodes[$nodeKey]); } } } else { // existing list with nodes, // add rideNodes with time constraint between existing nodes foreach ($rideNodeList->getRideNodes() as $listNode) { foreach ($workRideNodes as $nodeKey => $node) { if (!$listNode->previousNode) { if ($node->endMinute + $addTimePickup < $listNode->startMinute) { $rideNodeList->addRideNodeBeforeRideNode($node, $listNode); unset($workRideNodes[$nodeKey]); continue; } } else { if ($node->endMinute + $addTimePickup < $listNode->startMinute && $node->startMinute > $listNode->previousNode->endMinute + $addTimePickup) { $rideNodeList->addRideNodeBeforeRideNode($node, $listNode); unset($workRideNodes[$nodeKey]); continue; } } if (!$listNode->nextNode) { if ($node->startMinute > $listNode->endMinute + $addTimePickup) { $rideNodeList->addRideNodeAfterRideNode($node, $listNode); unset($workRideNodes[$nodeKey]); continue; } } else { if ($node->startMinute > $listNode->endMinute + $addTimePickup && $node->endMinute + $addTimePickup < $listNode->nextNode->startMinute) { $rideNodeList->addRideNodeAfterRideNode($node, $listNode); unset($workRideNodes[$nodeKey]); continue; } } } } } } //left Nodes are not feasible if (count($workRideNodes) > 1) { $rideConfiguration->setNotFeasibleNodes($workRideNodes); } return $rideConfiguration; }