/// <summary> /// Executes the request. If there are more waypoints than the batchSize value (default 25), only a MaxSolutions is set to 1, and Tolerances is set to null. /// </summary> /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time in seconds is sent.</param> /// <returns>A response containing the requested data.</returns> public override async Task <Response> Execute(Action <int> remainingTimeCallback) { Response response = null; if (WaypointOptimization != null && WaypointOptimization.HasValue && Waypoints.Count >= 2) { var wpHash = ServiceHelper.GetSequenceHashCode <SimpleWaypoint>(Waypoints); var optionHash = Enum.GetName(typeof(TspOptimizationType), WaypointOptimization); var mode = TravelModeType.Driving; DateTime?depart = null; if (RouteOptions != null) { optionHash += "|" + Enum.GetName(typeof(RouteTimeType), RouteOptions.TimeType); if (RouteOptions.TimeType == RouteTimeType.Departure && RouteOptions.DateTime != null && RouteOptions.DateTime.HasValue) { depart = RouteOptions.DateTime; optionHash += "|" + RouteOptions.DateTime.ToString(); } mode = RouteOptions.TravelMode; optionHash += "|" + Enum.GetName(typeof(TravelModeType), mode); } else { optionHash += "|" + Enum.GetName(typeof(RouteTimeType), RouteTimeType.Departure); } //Check to see if the waypoints have changed since they were last optimized. if (waypointsHash != wpHash || string.Compare(optimizationOptionHash, optionHash) != 0) { var tspResult = await TravellingSalesmen.Solve(Waypoints, mode, WaypointOptimization, depart, BingMapsKey); Waypoints = tspResult.OptimizedWaypoints; //Update the stored hashes to prevent unneeded optimizations in the future if not needed. waypointsHash = ServiceHelper.GetSequenceHashCode <SimpleWaypoint>(Waypoints); optimizationOptionHash = optionHash; } } var requestUrl = GetRequestUrl(); if (RouteOptions != null && RouteOptions.TravelMode == TravelModeType.Truck) { var requestBody = GetTruckPostRequestBody(); response = await ServiceHelper.MakeAsyncPostRequest <Route>(requestUrl, requestBody, remainingTimeCallback); } else { if (Waypoints.Count <= batchSize) { using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(requestUrl))) { return(ServiceHelper.DeserializeStream <Response>(responseStream)); } } //There is more waypoints than the batchSize value (default 25), break it up into multiple requests. Only allow a single route in the response and no tolerances. if (RouteOptions != null) { if (RouteOptions.MaxSolutions > 1) { RouteOptions.MaxSolutions = 1; } RouteOptions.Tolerances = null; } if (Waypoints == null) { throw new Exception("Waypoints not specified."); } else if (Waypoints.Count < 2) { throw new Exception("Not enough Waypoints specified."); } else if (Waypoints[0].IsViaPoint || Waypoints[Waypoints.Count - 1].IsViaPoint) { throw new Exception("Start and end waypoints must not be ViaWaypoints."); } int startIdx = 0; int endIdx = 0; var requestUrls = new List <string>(); while (endIdx < Waypoints.Count - 1) { requestUrls.Add(GetRequestUrl(startIdx, out endIdx)); startIdx = endIdx - 1; } var routes = new Route[requestUrls.Count]; Response errorResponse = null; Parallel.For(0, requestUrls.Count, (i) => { try { //Make the call synchronously as we are in a parrallel for loop and need this to block, otherwise the for loop will exist before the async code has completed. using (var responseStream = ServiceHelper.GetStreamAsync(new Uri(requestUrls[i])).GetAwaiter().GetResult()) { var r = ServiceHelper.DeserializeStream <Response>(responseStream); if (r != null) { if (r.ErrorDetails != null && r.ErrorDetails.Length > 0) { errorResponse = r; } else if (r.ResourceSets != null && r.ResourceSets.Length > 0 && r.ResourceSets[0].Resources != null && r.ResourceSets[0].Resources.Length > 0) { routes[i] = r.ResourceSets[0].Resources[0] as Route; } } if (i == 0) { response = r; } } } catch (Exception ex) { errorResponse = new Response() { ErrorDetails = new string[] { ex.Message } }; } }); //If any of the responses failed to process, do not merge results, return the error info. if (errorResponse != null) { return(errorResponse); } response.ResourceSets[0].Resources[0] = await MergeRoutes(routes); } return(response); }
/// <summary> /// Executes the request. If there are more waypoints than the batchSize value (default 25), only a MaxSolutions is set to 1, and Tolerances is set to null. /// </summary> /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time in seconds is sent.</param> /// <returns>A response containing the requested data.</returns> public override async Task <Response> Execute(Action <int> remainingTimeCallback) { Response response = null; if (WaypointOptimization != null && WaypointOptimization.HasValue && Waypoints.Count >= 2) { var wpHash = ServiceHelper.GetSequenceHashCode <SimpleWaypoint>(Waypoints); var optionHash = Enum.GetName(typeof(TspOptimizationType), WaypointOptimization); var mode = TravelModeType.Driving; DateTime?depart = null; if (RouteOptions != null) { optionHash += "|" + Enum.GetName(typeof(RouteTimeType), RouteOptions.TimeType); if (RouteOptions.TimeType == RouteTimeType.Departure && RouteOptions.DateTime != null && RouteOptions.DateTime.HasValue) { depart = RouteOptions.DateTime; optionHash += "|" + RouteOptions.DateTime.ToString(); } mode = RouteOptions.TravelMode; optionHash += "|" + Enum.GetName(typeof(TravelModeType), mode); } else { optionHash += "|" + Enum.GetName(typeof(RouteTimeType), RouteTimeType.Departure); } //Check to see if the waypoints have changed since they were last optimized. if (waypointsHash != wpHash || string.Compare(optimizationOptionHash, optionHash) != 0) { var tspResult = await TravellingSalesmen.Solve(Waypoints, mode, WaypointOptimization, depart, BingMapsKey); Waypoints = tspResult.OptimizedWaypoints; //Update the stored hashes to prevent unneeded optimizations in the future if not needed. waypointsHash = ServiceHelper.GetSequenceHashCode <SimpleWaypoint>(Waypoints); optimizationOptionHash = optionHash; } } var requestUrl = GetRequestUrl(); int startIdx = 0; int endIdx = 0; if (Waypoints.Count <= batchSize) { if (RouteOptions != null && RouteOptions.TravelMode == TravelModeType.Truck) { var requestBody = GetTruckPostRequestBody(startIdx, out endIdx); response = await ServiceHelper.MakeAsyncPostRequest(requestUrl, requestBody, remainingTimeCallback); } else { remainingTimeCallback?.Invoke(1); using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(requestUrl))) { response = ServiceHelper.DeserializeStream <Response>(responseStream); } } } else { //There is more waypoints than the batchSize value (default 25), break it up into multiple requests. Only allow a single route in the response and no tolerances. if (RouteOptions != null) { if (RouteOptions.MaxSolutions > 1) { RouteOptions.MaxSolutions = 1; } RouteOptions.Tolerances = null; } if (Waypoints == null) { throw new Exception("Waypoints not specified."); } else if (Waypoints.Count < 2) { throw new Exception("Not enough Waypoints specified."); } else if (Waypoints[0].IsViaPoint || Waypoints[Waypoints.Count - 1].IsViaPoint) { throw new Exception("Start and end waypoints must not be ViaWaypoints."); } var requestUrls = new List <string>(); var requestBodies = new List <string>(); while (endIdx < Waypoints.Count - 1) { if (RouteOptions != null && RouteOptions.TravelMode == TravelModeType.Truck) { requestUrls.Add(requestUrl); requestBodies.Add(GetTruckPostRequestBody(startIdx, out endIdx)); } else { requestUrls.Add(GetRequestUrl(startIdx, out endIdx)); requestBodies.Add(null); } startIdx = endIdx - 1; } if (remainingTimeCallback != null) { int batchProcessingTime = (int)Math.Ceiling((double)requestUrls.Count / (double)ServiceManager.QpsLimit); if (RouteOptions != null && RouteOptions.TravelMode == TravelModeType.Truck) { //Use an average of 4 seconds per batch for processing truck routes as multiplier for the processing time. //Other routes typically take less than a second and as such 1 second is used for those but isn't needed as a multiplier. batchProcessingTime *= 4; } remainingTimeCallback(batchProcessingTime); } var routes = new Route[requestUrls.Count]; var routeTasks = new List <Task>(); for (var i = 0; i < routes.Length; i++) { routeTasks.Add(CalculateRoute(requestUrls[i], requestBodies[i], i, routes)); } if (routeTasks.Count > 0) { await ServiceHelper.WhenAllTaskLimiter(routeTasks); } try { response = new Response() { StatusCode = 200, StatusDescription = "OK", ResourceSets = new ResourceSet[] { new ResourceSet() { Resources = new Resource[] { await MergeRoutes(routes) } } } }; } catch (Exception ex) { return(new Response() { StatusCode = 500, StatusDescription = "Error", ErrorDetails = new string[] { ex.Message } }); } } return(response); }