/// <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) { Response r = null; using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(GetRequestUrl())).ConfigureAwait(false)) { using (var sr = new StreamReader(responseStream)) { var s = sr.ReadToEnd(); //Replace "__type" with "type" for Entities to work around inconsistent logic used in Bing Maps REST APIs. s = s.Replace("\"__type\":\"Address\"", "\"type\":\"Address\"") .Replace("\"__type\":\"LocalBusiness\"", "\"type\":\"LocalBusiness\"") .Replace("\"__type\":\"Place\"", "\"type\":\"Place\""); var bytes = Encoding.UTF8.GetBytes(s); using (var stream = new MemoryStream(bytes)) { r = ServiceHelper.DeserializeStream <Response>(stream); } } } return(r); }
/// <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 data.</returns> public override async Task <Response> Execute(Action <int> remainingTimeCallback) { Stream responseStream = null; GetMetadata = true; if (Pushpins != null && Pushpins.Count > 18) { //Make a post request when there are more than 18 pushpins as there is a risk of URL becoming too large for a GET request. responseStream = await ServiceHelper.PostStringAsync(new Uri(GetPostRequestUrl()), GetPushpinsAsString(), null); } else { responseStream = await ServiceHelper.GetStreamAsync(new Uri(GetRequestUrl())); } if (responseStream != null) { var r = ServiceHelper.DeserializeStream <Response>(responseStream); responseStream.Dispose(); return(r); } return(null); }
/// <summary> /// Monitors the status of an async request. /// </summary> /// <param name="statusUrl">The status URL for the async request.</param> /// <param name="failedTries">The number of times the status check has failed consecutively.</param> /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time in seconds is sent.</param> /// <returns>The final async status when the request completed, had an error, or was not accepted.</returns> private static async Task <AsyncStatus> MonitorAsyncStatus(Uri statusUrl, int failedTries, Action <int> remainingTimeCallback) { AsyncStatus status = null; try { using (var rs = await ServiceHelper.GetStreamAsync(statusUrl).ConfigureAwait(false)) { var r = ServiceHelper.DeserializeStream <Response>(rs); if (r != null) { if (r.ErrorDetails != null && r.ErrorDetails.Length > 0) { throw new Exception(r.ErrorDetails[0]); } else if (r.ResourceSets != null && r.ResourceSets.Length > 0 && r.ResourceSets[0].Resources != null && r.ResourceSets[0].Resources.Length > 0 && r.ResourceSets[0].Resources[0] is AsyncStatus) { status = r.ResourceSets[0].Resources[0] as AsyncStatus; if (!status.IsCompleted && status.CallbackInSeconds > 0) { remainingTimeCallback?.Invoke(status.CallbackInSeconds); //Wait remaining seconds. await Task.Delay(TimeSpan.FromSeconds(status.CallbackInSeconds)).ConfigureAwait(false); return(await MonitorAsyncStatus(statusUrl, 0, remainingTimeCallback).ConfigureAwait(false)); } } } } } catch (Exception ex) { //Check to see how many times the status check has failed consecutively. if (failedTries < MaxStatusCheckRetries) { //Wait some time and try again. await Task.Delay(TimeSpan.FromSeconds(StatusCheckRetryDelay)).ConfigureAwait(false); return(await MonitorAsyncStatus(statusUrl, failedTries + 1, remainingTimeCallback).ConfigureAwait(false)); } else { status = new AsyncStatus() { ErrorMessage = "Failed to get status, and exceeded the maximium of " + MaxStatusCheckRetries + " retries. Error message: " + ex.Message, CallbackInSeconds = -1, IsCompleted = false }; } } //Should only get here is the request has completed, was not accepted or there was an error. return(status); }
//TODO: this is temporary until distance matrix Async response updated. /// <summary> /// Makes an Async request and monitors it till completion. /// </summary> /// <typeparam name="T">The type of resource to expect to be returned.</typeparam> /// <param name="requestUrl">REST URL request.</param> /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time in seconds is sent.</param> /// <returns>A completed response for a request.</returns> internal static async Task <Response> MakeAsyncGetRequest <T>(string requestUrl, Action <int> remainingTimeCallback) where T : Resource { Response response = null; using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(requestUrl)).ConfigureAwait(false)) { response = ServiceHelper.DeserializeStream <Response>(responseStream); return(await ProcessAsyncResponse <T>(response, remainingTimeCallback).ConfigureAwait(false)); } }
/// <summary> /// Makes an Async request and monitors it to completion. /// </summary> /// <param name="requestUrl">REST URL request.</param> /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time in seconds is sent.</param> /// <returns>A completed response for a request.</returns> internal static async Task <Response> MakeAsyncGetRequest(string requestUrl, Action <int> remainingTimeCallback) { Response response = null; using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(requestUrl))) { response = ServiceHelper.DeserializeStream <Response>(responseStream); return(await ProcessAsyncResponse(response, remainingTimeCallback)); } }
/// <summary> /// Makes an Async request and monitors it till completion. /// </summary> /// <typeparam name="T">The type of resource to expect to be returned.</typeparam> /// <param name="requestUrl">REST URL request.</param> /// <param name="requestBody">The post request body.</param> /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time in seconds is sent.</param> /// <returns>A completed response for a request.</returns> internal static async Task <Response> MakeAsyncPostRequest <T>(string requestUrl, string requestBody, Action <int> remainingTimeCallback) where T : Resource { Response response = null; using (var responseStream = await ServiceHelper.PostStringAsync(new Uri(requestUrl), requestBody, "application/json")) { response = ServiceHelper.DeserializeStream <Response>(responseStream); return(await ProcessAsyncResponse <T>(response, remainingTimeCallback)); } }
/// <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(); //TODO: change to async when supported using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(GetRequestUrl()))) { return(ServiceHelper.DeserializeStream <Response>(responseStream)); } //return await ServiceHelper.MakeAsyncGetRequest<IsochroneResponse>(requestUrl, remainingTimeCallback); }
/// <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 virtual async Task <Response> Execute(Action <int> remainingTimeCallback) { Response r = null; using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(GetRequestUrl()))) { r = ServiceHelper.DeserializeStream <Response>(responseStream); } return(r); }
private static async Task <Response> ProcessAsyncResponse <T>(Response response, Action <int> remainingTimeCallback) where T : Resource { if (response != null) { if (response.ErrorDetails != null && response.ErrorDetails.Length > 0) { throw new Exception(String.Join("", response.ErrorDetails)); } if (response.ResourceSets != null && response.ResourceSets.Length > 0 && response.ResourceSets[0].Resources != null && response.ResourceSets[0].Resources.Length > 0) { if (response.ResourceSets[0].Resources[0] is AsyncStatus && !string.IsNullOrEmpty((response.ResourceSets[0].Resources[0] as AsyncStatus).RequestId)) { var status = response.ResourceSets[0].Resources[0] as AsyncStatus; status = await ServiceHelper.ProcessAsyncStatus(status, remainingTimeCallback); if (status != null && status.IsCompleted && !string.IsNullOrEmpty(status.ResultUrl)) { try { using (var resultStream = await ServiceHelper.GetStreamAsync(new Uri(status.ResultUrl))) { var resource = ServiceHelper.DeserializeStream <T>(resultStream); response.ResourceSets[0].Resources[0] = resource; } } catch (Exception ex) { throw new Exception("There was an issue downloading and serializing the results. Results Download URL: " + status.ResultUrl + "\r\n" + ex.Message); } } return(response); } else if (response.ResourceSets[0].Resources[0] is AsyncStatus && !string.IsNullOrEmpty((response.ResourceSets[0].Resources[0] as AsyncStatus).ErrorMessage)) { throw new Exception((response.ResourceSets[0].Resources[0] as AsyncStatus).ErrorMessage); } else if (response.ResourceSets[0].Resources[0] is Resource && !(response.ResourceSets[0].Resources[0] is AsyncStatus)) { return(response); } } } throw new Exception("No response returned by service."); }
public override async Task <Response> Execute(Action <int> remainingTimeCallback) { Stream responseStream = null; responseStream = await ServiceHelper.GetStreamAsync(new Uri(GetRequestUrl())).ConfigureAwait(false); if (responseStream != null) { var r = ServiceHelper.DeserializeStream <Response>(responseStream); responseStream.Dispose(); return(r); } return(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 data.</returns> public override async Task <Response> Execute(Action <int> remainingTimeCallback) { Stream responseStream = null; if (Points != null && Points.Count > 50) { //Make a post request when there are more than 50 points as there is a risk of URL becoming too large for a GET request. responseStream = await ServiceHelper.PostStringAsync(new Uri(GetPostRequestUrl()), GetPointsAsString(), null).ConfigureAwait(false); } else { responseStream = await ServiceHelper.GetStreamAsync(new Uri(GetRequestUrl())).ConfigureAwait(false); } if (responseStream != null) { var r = ServiceHelper.DeserializeStream <Response>(responseStream); responseStream.Dispose(); return(r); } return(null); }
/// <summary> /// Executes the request. /// </summary> /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time is sent.</param> /// <returns>A response containing the requested distance matrix.</returns> public override async Task <Response> Execute(Action <int> remainingTimeCallback) { //Make sure all origins and destinations are geocoded. await GeocodeWaypoints(); var requestUrl = GetRequestUrl(); var requestBody = GetPostRequestBody(); Response response = null; using (var responseStream = await ServiceHelper.PostStringAsync(new Uri(requestUrl), requestBody, "application/json")) { response = ServiceHelper.DeserializeStream <Response>(responseStream); } if (response != null && response.ErrorDetails != null && response.ErrorDetails.Length > 0) { throw new Exception("Error: " + response.ErrorDetails[0]); } if (response != null && response.ResourceSets != null && response.ResourceSets.Length > 0 && response.ResourceSets[0].Resources != null && response.ResourceSets[0].Resources.Length > 0) { if (response.ResourceSets[0].Resources[0] is DistanceMatrixAsyncStatus && !string.IsNullOrEmpty((response.ResourceSets[0].Resources[0] as DistanceMatrixAsyncStatus).RequestId)) { var status = response.ResourceSets[0].Resources[0] as DistanceMatrixAsyncStatus; var statusUrl = new Uri(status.CallbackUrl); //var statusUrl = new Uri(this.Domain + "Routes/DistanceMatrixAsyncCallback?requestId=" + status.RequestId + "&key=" + this.BingMapsKey); if (status.CallbackInSeconds > 0 || !status.IsCompleted || string.IsNullOrEmpty(status.ResultUrl)) { remainingTimeCallback?.Invoke(status.CallbackInSeconds); //Wait remaining seconds. await Task.Delay(TimeSpan.FromSeconds(status.CallbackInSeconds)); status = await MonitorAsyncStatus(statusUrl, 0, remainingTimeCallback); } if (status != null) { if (status.IsCompleted && !string.IsNullOrEmpty(status.ResultUrl)) { try { using (var resultStream = await ServiceHelper.GetStreamAsync(new Uri(status.ResultUrl))) { DistanceMatrix dm = ServiceHelper.DeserializeStream <DistanceMatrix>(resultStream); response.ResourceSets[0].Resources[0] = dm; //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.ToArray(); dm.Destinations = this.Destinations.ToArray(); } } catch (Exception ex) { response.ResourceSets[0].Resources[0] = new DistanceMatrix() { ErrorMessage = "There was an issue downloading and serializing the results. Results Download URL: " + status.ResultUrl }; } } else if (!status.IsAccepted) { response.ResourceSets[0].Resources[0] = new DistanceMatrix() { ErrorMessage = "The request was not accepted." }; } else if (!string.IsNullOrEmpty(status.ErrorMessage)) { response.ResourceSets[0].Resources[0] = new DistanceMatrix() { ErrorMessage = status.ErrorMessage }; } } return(response); } else if (response.ResourceSets[0].Resources[0] is DistanceMatrix && (response.ResourceSets[0].Resources[0] as DistanceMatrix).Results != null) { DistanceMatrix 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.ToArray(); dm.Destinations = this.Destinations.ToArray(); if (dm.Results != null) { return(response); } else if (!string.IsNullOrEmpty(dm.ErrorMessage)) { var msg = "Error: " + (response.ResourceSets[0].Resources[0] as DistanceMatrix).ErrorMessage; throw new Exception(msg); } } else if (response.ResourceSets[0].Resources[0] is DistanceMatrixAsyncStatus && !string.IsNullOrEmpty((response.ResourceSets[0].Resources[0] as DistanceMatrixAsyncStatus).ErrorMessage)) { var msg = "Error: " + (response.ResourceSets[0].Resources[0] as DistanceMatrixAsyncStatus).ErrorMessage; throw new Exception(msg); } else if (response.ResourceSets[0].Resources[0] is DistanceMatrix && !string.IsNullOrEmpty((response.ResourceSets[0].Resources[0] as DistanceMatrix).ErrorMessage)) { var msg = "Error: " + (response.ResourceSets[0].Resources[0] as DistanceMatrix).ErrorMessage; throw new Exception(msg); } } 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); }
//TODO: this is temporary until distance matrix Async response updated. private static async Task <Response> ProcessAsyncResponse <T>(Response response, Action <int> remainingTimeCallback) where T : Resource { if (response != null) { if (response.ErrorDetails != null && response.ErrorDetails.Length > 0) { throw new Exception(String.Join("", response.ErrorDetails)); } if (Response.HasResource(response)) { var res = Response.GetFirstResource(response); if (res is AsyncStatus && !string.IsNullOrEmpty((res as AsyncStatus).RequestId)) { var status = res as AsyncStatus; status = await ServiceHelper.ProcessAsyncStatus(status, remainingTimeCallback).ConfigureAwait(false); if (status != null && status.IsCompleted && !string.IsNullOrEmpty(status.ResultUrl)) { try { using (var resultStream = await ServiceHelper.GetStreamAsync(new Uri(status.ResultUrl)).ConfigureAwait(false)) { if (typeof(T) == typeof(DistanceMatrix)) { //There is a bug in the distance matrix service that when some options are set, the response isn't wrapped with a resourceSet->resources like all other services. using (var sr = new StreamReader(resultStream)) { var r = sr.ReadToEnd(); //Remove first character from string. r = r.Remove(0, 1); //Add namespace type to JSON object. r = "{\"__type\":\"DistanceMatrix:http://schemas.microsoft.com/search/local/ws/rest/v1\"," + r; var bytes = Encoding.UTF8.GetBytes(r); using (var stream = new MemoryStream(bytes)) { var resource = ServiceHelper.DeserializeStream <T>(stream); response.ResourceSets[0].Resources[0] = resource; } } } else if (typeof(T) == typeof(SnapToRoadResponse)) { //Snap to road for some reason includes a full resource set response while other async services don't. response = ServiceHelper.DeserializeStream <Response>(resultStream); } else { var resource = ServiceHelper.DeserializeStream <T>(resultStream); response.ResourceSets[0].Resources[0] = resource; } } } catch (Exception ex) { throw new Exception("There was an issue downloading and serializing the results. Results Download URL: " + status.ResultUrl + "\r\n" + ex.Message); } } return(response); } else if (res is AsyncStatus && !string.IsNullOrEmpty((res as AsyncStatus).ErrorMessage)) { throw new Exception((res as AsyncStatus).ErrorMessage); } else if (res is Resource && !(res is AsyncStatus)) { return(response); } } } throw new Exception("No response returned by service."); }
/// <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 is sent.</param> /// <returns>A response containing the requested data.</returns> public override async Task <Response> Execute(Action <int> remainingTimeCallback) { if (Waypoints.Count <= batchSize) { using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(GetRequestUrl()))) { 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 response = null; 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); }