/// <summary> /// Computes the simple backhaul count for the given route solution /// </summary> /// <param name="routeSolution">the route solution from which backhauls are counted</param> /// <returns>the sum of JobNodes plus DriverNodes minus 1</returns> public int CalculateNumberOfBackhauls(NodeRouteSolution routeSolution) { int result = 0; result = routeSolution.Nodes.Count - 1; return(result); }
/// <summary> /// Computes the TruckPerformanceStatistics for a given route solution /// </summary> /// <param name="routeSolution">The route from which statistics are generated</param> /// <returns>The performance statistics for the truck</returns> public TruckPerformanceStatistics CalculateTruckPerformanceStatistics(NodeRouteSolution routeSolution) { var segmentStats = GetRouteSegmentStats(routeSolution); // calculate the statistics by truck state var statsByTructState = (from segment in segmentStats group segment by segment.TruckState into g select new { TruckState = g.Key, Statistics = g.Aggregate(new RouteStatistics(), (seed, segment) => seed + segment.Statistics) }) .ToDictionary(key => key.TruckState, value => value.Statistics); // compute the sum of all the route statistics var totalRouteStatistics = segmentStats.Aggregate(new RouteStatistics(), (seed, segment) => seed + segment.Statistics); var performanceStatistics = CalculatePerformanceStatistics(routeSolution); var truckStatistics = new TruckPerformanceStatistics { RouteStatisticsByTruckState = statsByTructState, RouteStatistics = totalRouteStatistics, PerformanceStatistics = performanceStatistics, RouteSegmentStatistics = segmentStats }; return(truckStatistics); }
///// <summary> ///// Creates a route solution from a list of nodes ///// </summary> ///// <param name="nodes"></param> ///// <returns></returns> public NodeRouteSolution CreateRouteSolution(IEnumerable <INode> nodes, DriverNode driverNode) { var routeSolution = new NodeRouteSolution { DriverNode = driverNode, StartTime = driverNode.Driver.EarliestStartTime, Nodes = nodes.ToList() }; var allNodes = routeSolution.AllNodes; // calculate route statistics for (int i = 0; i < allNodes.Count; i++) { // create node plan var node = allNodes[i]; // add node trip length routeSolution.RouteStatistics += node.RouteStatistics; var previousNode = i == 0 ? null : allNodes[i - 1]; if (previousNode != null) { var statistics = CalculateRouteStatistics(previousNode, node); routeSolution.RouteStatistics += statistics; } } return(routeSolution); }
/// <summary> /// Returns true if the given route solution is feasable within time windows and exit criteria /// </summary> /// <param name="nodeRouteSolution"></param> /// <returns></returns> public bool IsFeasableRouteSolution(NodeRouteSolution nodeRouteSolution) { var driverNode = nodeRouteSolution.DriverNode; var currentNodeEndTime = driverNode.Driver.EarliestStartTime; var cumulativeRouteStatistics = new RouteStatistics(); var allNodes = nodeRouteSolution.AllNodes; for (int i = 0; i < allNodes.Count - 1; i++) { var nodeTiming = _nodeService.GetNodeTiming(allNodes[i], allNodes[i + 1], currentNodeEndTime, cumulativeRouteStatistics); if (nodeTiming.IsFeasableTimeWindow) { // is it a feasable route var lastConnection = _nodeService.GetNodeConnection(nodeTiming.Node, driverNode); var finalRouteStatistics = nodeTiming.CumulativeRouteStatistics + lastConnection.RouteStatistics; if (_routeExitFunction.ExeedsExitCriteria(finalRouteStatistics, driverNode.Driver)) { return(false); } } else { return(false); } currentNodeEndTime = nodeTiming.EndExecutionTime; cumulativeRouteStatistics = nodeTiming.CumulativeRouteStatistics; } return(true); }
public IList <RouteStop> GetRouteStopsForRouteSolution(NodeRouteSolution routeSolution) { var allNodes = routeSolution.AllNodes; var routeStops = new List <RouteStop>(); routeStops.AddRange(allNodes[0].RouteStops); // calculate route statistics for (int i = 1; i < allNodes.Count; i++) { // create node plan var node = allNodes[i]; var previousNode = allNodes[i - 1]; if (previousNode != null) { var nodeConnection = _nodeService.GetNodeConnection(previousNode, node); if (nodeConnection.RouteStops != null) { routeStops.AddRange(nodeConnection.RouteStops); } } routeStops.AddRange(node.RouteStops); } return(routeStops); }
/// <summary> /// Gets the route segment stats. /// </summary> /// <param name="routeSolution">The route solution.</param> /// <returns>a list of statistics for each leg of the route</returns> public IList <RouteSegmentStatistics> GetRouteSegmentStats(NodeRouteSolution routeSolution) { var startTime = routeSolution.StartTime; var routeStops = _routeService.GetRouteStopsForRouteSolution(routeSolution); var segmentStats = _routeStopService.CalculateRouteSegmentStatistics(startTime, routeStops); return(segmentStats); }
/// <summary> /// Computes the performance statistics for a given route solution /// </summary> /// <param name="routeSolution">The solution from which the report is generated</param> /// <returns>the statistics generated from the route</returns> private PerformanceStatistics CalculatePerformanceStatistics(NodeRouteSolution routeSolution) { var result = new PerformanceStatistics() { NumberOfJobs = routeSolution.JobCount, DriverDutyHourUtilization = routeSolution.RouteStatistics.TotalTime.TotalHours / routeSolution.DriverNode.Driver.AvailableDutyTime.TotalHours, DriverDrivingUtilization = routeSolution.RouteStatistics.TotalTravelTime.TotalHours / routeSolution.DriverNode.Driver.AvailableDrivingTime.TotalHours, DrivingTimePercentage = routeSolution.RouteStatistics.TotalTravelTime.TotalHours / routeSolution.RouteStatistics.TotalTime.TotalHours, WaitingTimePercentage = routeSolution.RouteStatistics.TotalIdleTime.TotalHours / routeSolution.RouteStatistics.TotalTime.TotalHours, NumberOfBackhauls = CalculateNumberOfBackhauls(routeSolution), NumberOfLoadmatches = CalculateNumberOfLoadMatches(routeSolution) }; return(result); }
public NodeRouteSolution GetBestSolution(NodeRouteSolution left, NodeRouteSolution right) { double prioritySumLeft = left.AllNodes.Sum(f => f.Priority); double prioritySumRight = right.AllNodes.Sum(f => f.Priority); int priorityCompareResult = prioritySumLeft.CompareTo(prioritySumRight); if (priorityCompareResult == 0) { return(_statisticsService.CompareRouteStatistics(left.RouteStatistics, right.RouteStatistics) > 0 ? right : left); } // return the solution with the highest priority sum return(priorityCompareResult > 0 ? left : right); }
/// <summary> /// Gets the best solution between a new list of <see cref="INode"/> and a current best <see cref="NodeRouteSolution"/> /// </summary> /// <param name="nodes"></param> /// <param name="driverNode"> </param> /// <param name="bestSolution"></param> /// <returns>the best solution</returns> public NodeRouteSolution GetBestFeasableSolution(IEnumerable <INode> nodes, DriverNode driverNode, NodeRouteSolution bestSolution) { // create solution var routeSolution = CreateRouteSolution(driverNode, nodes); // check feasibility if (IsFeasableRouteSolution(routeSolution)) { if (bestSolution != null) { routeSolution = GetBestSolution(bestSolution, routeSolution); } } else { routeSolution = bestSolution; } return(routeSolution); }
public NodeRouteSolution GetBestSolution(NodeRouteSolution left, NodeRouteSolution right) { double prioritySumLeft = left.AllNodes.Sum(f => f.Priority); double prioritySumRight = right.AllNodes.Sum(f => f.Priority); // set driver count var leftRs = left.RouteStatistics; leftRs.DriversWithAssignments = left.Nodes.Count > 0 ? 1 : 0; left.RouteStatistics = leftRs; var rightRs = right.RouteStatistics; rightRs.DriversWithAssignments = right.Nodes.Count > 0 ? 1 : 0; right.RouteStatistics = rightRs; //if (leftRs.DriversWithAssignments > 0 || rightRs.DriversWithAssignments > 0) //{ // ; //} int priorityCompareResult = prioritySumLeft.CompareTo(prioritySumRight); if (priorityCompareResult == 0) { return(_routeStatisticsComparer.Compare(left.RouteStatistics, right.RouteStatistics) > 0 ? right : left); } return(_routeStatisticsComparer.Compare(left.RouteStatistics, right.RouteStatistics) > 0 ? right : left); // return the solution with the highest priority sum //return priorityCompareResult > 0 ? left : right; var result = _routeStatisticsComparer.Min(left, right); return(result); //return _routeStatisticsComparer.Compare(left.RouteStatistics, right.RouteStatistics) > 0 ? right : left; }
/// <summary> /// Computes the number of load maches for the given route solution /// </summary> /// <param name="routeSolution">the solution from which the count is retrieved</param> /// <returns>the count of liveloading followed by live unloading stops along the route</returns> public int CalculateNumberOfLoadMatches(NodeRouteSolution routeSolution) { int result = 0; // iterates through node collection foreach (var node in routeSolution.Nodes) { bool hasLiveUnloading = false; bool hasBoth = false; foreach (var routeStop in node.RouteStops) { if (routeStop.StopAction == StopActions.LiveUnloading) { hasLiveUnloading = true; } else { if (hasLiveUnloading && routeStop.StopAction == StopActions.LiveLoading) { hasBoth = true; } else if (hasLiveUnloading && routeStop.StopAction != StopActions.LiveLoading) { hasLiveUnloading = false; hasBoth = false; } } if (hasBoth) { result++; hasLiveUnloading = false; hasBoth = false; } } } return(result); }
public IList <RouteStop> GetRouteStopsForRouteSolution(NodeRouteSolution routeSolution) { var allNodes = routeSolution.AllNodes; var routeStops = new List <RouteStop>(); foreach (var rs in allNodes[0].RouteStops) { routeStops.Add(rs); } // calculate route statistics for (int i = 1; i < allNodes.Count; i++) { // create node plan var node = allNodes[i]; routeStops.AddRange(node.RouteStops); } return(routeStops); }
/// <summary> /// Creates our route assignments by processing all the drivers and getting their routes /// </summary> /// <returns></returns> public virtual Solution ProcessDrivers(IList <DriverNode> driverNodes, Driver placeholderDriver, IList <INode> jobNodes) { var solution = new Solution(); if (jobNodes.Count == 0) { return(solution); } var availableNodes = jobNodes; var realDriverNodes = driverNodes.Where(f => f.Driver != placeholderDriver) .OrderBy(f => _randomNumberGenerator.Next()); // random sort order var placeholderDriverNodes = driverNodes.Where(f => f.Driver == placeholderDriver); // going through all the real drivers foreach (var driverNode in realDriverNodes) { // get the route solution for this particular driver var nodeRouteSolution = new NodeRouteSolution(); nodeRouteSolution = GenerateIterativeRouteSolution(availableNodes, driverNode); // insert solution solution.RouteSolutions.Add(nodeRouteSolution); // update available nodes based on new solution var selectedIds = nodeRouteSolution.Nodes.Select(p => p.Id).ToList(); availableNodes = availableNodes.Where(p => !selectedIds.Contains(p.Id)).ToList(); if (availableNodes.Count == 0) { break; } } // try using placeholder drivers if (availableNodes.Count > 0) { foreach (var driverNode in placeholderDriverNodes) { // get the route solution for this particular driver var nodeRouteSolution = GenerateIterativeRouteSolution(availableNodes, driverNode); // don't bother if we didn't generate a solution if (nodeRouteSolution.Nodes.Count == 0) { break; } // insert solution solution.RouteSolutions.Add(nodeRouteSolution); // update available nodes based on new solution availableNodes = availableNodes.Where(n => !nodeRouteSolution.Nodes.Contains(n)).ToList(); if (availableNodes.Count == 0) { break; } } } // Add unassigned job nodes solution.UnassignedJobNodes.AddRange(availableNodes); return(solution); }
///// <summary> ///// Creates a route solution from a list of nodes ///// </summary> ///// <param name="nodes"></param> ///// <returns></returns> public NodeRouteSolution CreateRouteSolution(DriverNode driverNode, IEnumerable <INode> nodes) { var startTime = driverNode.Driver.EarliestStartTimeSpan; var firstJobNode = nodes.FirstOrDefault(); // adjust the start time if (firstJobNode != null) { // there may have been waiting for the driver to leave, do not count that as idle time // update the currentTime to the JobNode WindowEnd, less travel time var connection = _nodeService.GetNodeConnection(driverNode, nodes.First()); if (firstJobNode.WindowEnd.Subtract(connection.RouteStatistics.TotalTravelTime) > startTime) { startTime = firstJobNode.WindowEnd.Subtract(connection.RouteStatistics.TotalTravelTime); } } var routeSolution = new NodeRouteSolution { DriverNode = driverNode, StartTime = startTime, Nodes = nodes.ToList() }; var allNodes = routeSolution.AllNodes; var currentTime = startTime; // calculate route statistics for (int i = 0; i < allNodes.Count; i++) { // create node plan var node = allNodes[i]; var previousNode = i > 0 ? allNodes[i - 1] : null; // insert waiting time as per JobNode windows if (i >= 1 && node is JobNode && previousNode is JobNode) { // there may have been waiting for the driver to leave, do not count that as idle time // update the currentTime to the JobNode WindowEnd, less travel time var connection = _nodeService.GetNodeConnection(previousNode, node); if (node.WindowEnd.Subtract(connection.RouteStatistics.TotalTravelTime) > currentTime) { // waiting found var waitingTime = node.WindowEnd.Subtract(currentTime); var currentStatistics = routeSolution.RouteStatistics; currentStatistics.TotalIdleTime = currentStatistics.TotalIdleTime.Add(waitingTime); routeSolution.RouteStatistics = currentStatistics; currentTime = currentTime.Add(waitingTime); } } // add node trip length var stats = _routeStatisticsService.GetRouteStatistics(node, currentTime, previousNode); routeSolution.RouteStatistics += stats; // currentTime = startTime + routeSolution.RouteStatistics.TotalTime; if (previousNode != null) { var statistics = _routeStatisticsService.GetRouteStatistics(previousNode, node, currentTime); routeSolution.RouteStatistics += statistics; } currentTime = startTime + routeSolution.RouteStatistics.TotalTime; } return(routeSolution); }