/// <summary> /// Executes the request. /// </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) { var requestUrl = GetRequestUrl(); var requestBody = GetPostRequestBody(); return(await ServiceHelper.MakeAsyncPostRequest <Route>(requestUrl, requestBody, remainingTimeCallback).ConfigureAwait(false)); }
/// <summary> /// Calculates a route use a request URL and adds it to an array of route results. This is used when batching multiple route calls together when supporting long routes that have more than 25 waypoints. /// </summary> /// <param name="requestUrl">The request URL.</param> /// <param name="body">The JSON POST body.</param> /// <param name="idx">The route index in the batch.</param> /// <param name="routes">The arrary in which the batch route results are stored.</param> /// <returns>A task for calculating a route as part of a batch.</returns> private Task CalculateRoute(string requestUrl, string body, int idx, Route[] routes) { return(Task.Run(() => { try { Response r = null; //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. if (!string.IsNullOrEmpty(body)) { r = ServiceHelper.MakeAsyncPostRequest(requestUrl, body, null).GetAwaiter().GetResult(); } else { using (var responseStream = ServiceHelper.GetStreamAsync(new Uri(requestUrl)).GetAwaiter().GetResult()) { r = ServiceHelper.DeserializeStream <Response>(responseStream); } } if (r != null && r.ErrorDetails == null && r.ResourceSets != null && r.ResourceSets.Length > 0 && r.ResourceSets[0].Resources != null && r.ResourceSets[0].Resources.Length > 0) { routes[idx] = r.ResourceSets[0].Resources[0] as Route; return; } } catch { } routes[idx] = null; })); }
/// <summary> /// Executes the request. /// </summary> /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time in seconds is sent.</param> /// <returns>A response containing the requested distance matrix.</returns> public override async Task <Response> Execute(Action <int> remainingTimeCallback) { if (TravelMode != TravelModeType.Truck) { //Make sure all origins and destinations are geocoded. await GeocodeWaypoints(); var requestUrl = GetRequestUrl(); var requestBody = GetPostRequestBody(); var response = await ServiceHelper.MakeAsyncPostRequest <DistanceMatrix>(requestUrl, requestBody, remainingTimeCallback); var dm = response.ResourceSets[0].Resources[0] as DistanceMatrix; //TODO: Overwrite origins/destinations for now as we have added support for geocoding in this library, but this is not yet supported by the Distance Matirx API. dm.Origins = this.Origins; if (this.Destinations != null) { dm.Destinations = this.Destinations; } if (dm.Results != null) { return(response); } //else if (!string.IsNullOrEmpty(dm.ErrorMessage)) //{ // throw new Exception(dm.ErrorMessage); //} } else { var requestUrl = GetRequestUrl(); //Generate truck routing based distnace matrix by wrapping routing service. return(await new Extensions.TruckDistanceMatrixGenerator().Calculate(this, remainingTimeCallback)); } return(null); }
/// <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); }