/// <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);
        }
示例#2
0
        /// <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;
            }));
        }
示例#3
0
        /// <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));
            }
        }
示例#6
0
        /// <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));
            }
        }
示例#8
0
        /// <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);
        }
示例#12
0
        /// <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);
        }
示例#13
0
        /// <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);
        }
示例#14
0
        /// <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.");
        }
示例#16
0
        /// <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);
        }
示例#17
0
        /// <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);
        }