Transportation management (TMS) mileage engine based on Bing Maps


Transportation management module (TMS) includes a number of extension points that allow for custom algorithms for tasks such as calculations for rates and transit time or mileage on a specific route. Dynamics AX 2013 R3 comes with a number of engines, including a point-to-point mileage engine that is defined as a Microsoft.Dynamics.Ax.Tms.Bll.P2PMileageEngine engine type. The point-to-point mileage engine requires that data is defined in AX in order to determine mileage between two addresses. This engine is good for smaller size distribution networks, but can become expensive in maintenance when working with a large number of destinations (for example, for use with an online retail store). For large distribution networks it makes sense to use external services, such as Bing Maps.

This blog post describes how to build a custom engine that can retrieve mileage data by using the Bing Maps SOAP services. There are other services that can be used for this purpose, for example the Bing Maps REST services, but these are not described in this document.

The following article provides more information about the Bing Maps SOAP services:
http://msdn.microsoft.com/en-us/library/dd221354.aspx

The following article provides more information about the Bing Maps REST services:
http://msdn.microsoft.com/en-us/library/ff701713.aspx

For more information about how to build a custom TMS engine, read following whitepaper:
http://www.microsoft.com/en-us/download/details.aspx?id=43385

The zipped source code of the discussed C# code is available for download as this blog file attachment CustomTMSEngines.zip.

Prerequisites

Despite installing Visual Studio tools for AX, we will need to create access keys in order to consume Bing Maps services. For commercial purposes we would need to create an enterprise key, but for testing a free trial key is enough. You can create a new key on following portal:

https://www.bingmapsportal.com/

Note that this requires a valid Live ID account.

The new key will be displayed in your profile. The following illustration shows an example.

Implement a custom engine

To implement a custom engine, follow these steps:

1.       Open the following project in the AOT:

\Visual Studio Projects\C Sharp Projects\Microsoft.Dynamics.AX.Tms
in Visual Studio.

2.       Add a basic TMS engine implementation project to your solution (C# class library). Let’s call the project CustomTMSEngines. Add reference to Microsoft.Dynamics.AX.Tms and add the project to AOT. Configure it to be deployed on AOS.

For step-by-step instructions for creating such a project, see the whitepaper titled Implementing and Deploying Transportation Management Engines for Microsoft Dynamics AX 2012 R3. The paper describes how to build simple TMS engines.

3.       Add service references to Bing Maps geocode service and Bing Maps route service.

To do that, follow these steps:

a.       Select the project node in Visual Studio

b.      Context menu>Add Service Reference

c.       Put this URL to “Address” and select “Go”:
http://dev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc?wsdl

d.      Name it BingMapsGeocodeService

e.      Click OK

f.        Repeat steps A-E for the following WSDL, and name it BingMapsRouteService:
http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc?wsdl

4.       Your Solution Explorer should now look like this:

 5.       Create a new class called BingMapsServicesClient.

The purpose of this class is to retrieve the distance from address A to address B by processing requests and responses from the Bing Maps service. The process consists of two steps:

a.       Retrieve geo-locations for address A and address B

b.      Retrieve distance from geo-location of A to B

For now, let’s just implement the constructor with one parameter “key” that will be part of the class state. The key parameter holds the value of the authentication key that we created earlier using our Live ID account.

///<summary>
/// Provides access to Bing Maps services.
///</summary>
internal class BingMapsServicesClient
{
    private string key;

    ///<summary>
    /// Creates a new instance of <c>BingMapsServicesClient</c> class.
   
///</summary>
   
///<param name=”key”>Bing Maps services access key.</param>
    public BingMapsServicesClient(string key)
    {
        this.key = key;
    }
}

 6.       The next step is to add a method to BindMapsServicesClient, which will allow us to retrieve geo-location, based on address string.

 

 

private BingMapsRouteService.Location GeolocationAddress(string address)
{
    BingMapsRouteService.Location result = null;
    GeocodeRequest geocodeRequest = new GeocodeRequest(); 

    // Set the credentials using a valid Bing Maps key
    geocodeRequest.Credentials = new BingMapsGeocodeService.Credentials();
    geocodeRequest.Credentials.ApplicationId = this.key; 

    // Set the full address query
    geocodeRequest.Query = address;
    // Set the options to only return high confidence results 
    ConfidenceFilter[] filters = new ConfidenceFilter[1];
    filters[0] = new ConfidenceFilter();
    filters[0].MinimumConfidence = BingMapsGeocodeService.Confidence.High;

    // Add the filters to the options
    GeocodeOptions geocodeOptions = new GeocodeOptions();
    geocodeOptions.Filters = filters;
    geocodeRequest.Options = geocodeOptions;

    // Make the geocode request
    Binding binding = new BasicHttpBinding();
    EndpointAddress endpointAddress = new EndpointAddress(@”http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc”);

    GeocodeServiceClient geocodeService = new GeocodeServiceClient(binding, endpointAddress);
    GeocodeResponse geocodeResponse = geocodeService.Geocode(geocodeRequest); 

    if (geocodeResponse.Results.Length > 0)
    {
        result = new BingMapsRouteService.Location()
        {
            Latitude = geocodeResponse.Results[0].Locations[0].Latitude,
            Longitude = geocodeResponse.Results[0].Locations[0].Longitude
        };
    }
 
    return result;
}

 

There are a few interesting things in this method that are worth mentioning. First, the method signature contains only one string parameter for address, which is treated as query for the Bing Maps service. The format of this address must enable Bing Maps to identify the address. The best way to test whether your query fits this format is to test it through your web browser at http://www.bing.com/maps/

Second, the endpoint address URI is hardcoded in the method. Typically endpoint and bindings are defined in the application configuration file. This may not be the most convenient solution for AX because configuration changes (for example, switching between production and test service URI) would require that the AOS is restarted. The best approach here would probably be to query the URI from AX database using AX Managed Interop. A convenient way to do this is to use the proxy classes for AX tables and LINQ to AX for building queries. The following article provides information about working with LINQ to AX: http://msdn.microsoft.com/en-us/library/jj677293.aspx.

If we decide to source the URI from AX, we need to keep security in mind and treat the URI as a protected resource and do the proper threat modeling.

7.       The next step is to add the method for retrieving mileage between two geographical locations. Add the following method to the BingMapsServicesClient class.

 

private double RetrieveDistance(
    BingMapsRouteService.Location locationFrom,
    BingMapsRouteService.Location locationTo)
{
    BingMapsRouteService.RouteRequest routeRequest = new BingMapsRouteService.RouteRequest();
    routeRequest.Credentials = new BingMapsRouteService.Credentials();
    routeRequest.Credentials.ApplicationId = this.key;
    routeRequest.Waypoints = new Waypoint[2]
    {
        new Waypoint() { Description = “Start”, Location = locationFrom },
        new Waypoint() { Description = “End”, Location = locationTo }
    };

    Binding binding = new BasicHttpBinding();
    EndpointAddress endpointAddress = new EndpointAddress(@”http://dev.virtualearth.net/webservices/v1/routeservice/routeservice.svc”); 

    RouteServiceClient routeService = new RouteServiceClient(binding, endpointAddress);
   
RouteResponse routeResponse = routeService.CalculateRoute(routeRequest);
    return routeResponse.Result.Summary.Distance / 1.609344;
}

 

 Again, this method uses a hardcoded URI for the route service, but it can be changed in a similar way as the GeolocationAddress method. Note that the distance that is retrieved from the Bing Maps service uses metric units. To convert kilometers to miles we divided it by the fixed factor.

 8.       To ensure that our code can compile we need proper imports. Add the following imports to BingMapsMileageEngine.cs:

    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using CustomTMSEngines.BingMapsGeocodeService;
    using CustomTMSEngines.BingMapsRouteService;

 

 9.       The last part that is missing in the BingMapsServicesClient class is a public method that will allow us to call the retrieved distance between two addresses without having to couple the consumer with the Bing Maps service proxy object model. The following method does this. 

///<summary>
/// Retrieves address between two fully specificed addresses.
///</summary>
///<param name=”addressFrom”>Origin address.</param>
///<param name=”addressTo”>Destination address/</param>

///<returns>Distance specified in miles.</returns>

public double RetrieveDistance(string addressFrom, string addressTo)

{

    BingMapsRouteService.Location locationStart = this.GeolocationAddress(addressFrom);
   
BingMapsRouteService.Location locationEnd = this.GeolocationAddress(addressTo);

    return RetrieveDistance(locationStart, locationEnd);
}

 10.   The next part is to implement is the actual mileage TMS engine that will consume the functionality provided by the BingMapsServiceClient class. The following code block contains the full implementation of this class. 

namespace CustomTMSEngines
{
    using System;
    using System.ServiceModel;
    using System.Xml.Linq;
    using Microsoft.Dynamics.Ax.Tms.Bll;
    using Microsoft.Dynamics.Ax.Tms.Data;
    using Microsoft.Dynamics.Ax.Tms.Utility;
    using Microsoft.Dynamics.Ax.Tms;

    ///<summary>
    /// The <c>BingMapsMileageEngine</c> class retrieves mileage data using Bing Maps services. 
    ///</summary>
    class BingMapsMileageEngine : IMileageEngine
    {
        private string mileageEngineCode;

        ///<summary>
        /// Initializes the engine instance.
        ///</summary>
        ///<param name=”mileageEngine”></param>
        public void Initialize(TMSMileageEngine mileageEngine)
        {
            mileageEngineCode = mileageEngine.MileageEngineCode;
        }

        ///<summary>
        /// Retrieves mileage.
        ///</summary>
        ///<param name=”transactionFacade”>TMS transaction facade.</param>
        ///<param name=”currentElement”>XML element to for which to apply the mileage result.</param>
        ///<returns>The mileage response.</returns>
        public MileageHelperResponse RetrieveMiles(TransactionFacade transactionFacade, XElement currentElement)
        {
            MileageHelperResponse result = new MileageHelperResponse(this.mileageEngineCode);
            string key = YOUR BING MAPS ACCESS KEY IS ASSIGNED HERE;

            XElement pickUp = currentElement.GetAddress(AddTypeXmlConstants.Pickup);
            XElement dropOff = currentElement.GetAddress(AddTypeXmlConstants.DropOff);
            BingMapsServicesClient bingMapsServicesClient = new BingMapsServicesClient(key);

            try
            {
                string addressFrom = this.retrieveAddress(pickUp);
                string addressTo = this.retrieveAddress(dropOff);
   
            result.Miles = Convert.ToDecimal(bingMapsServicesClient.RetrieveDistance(addressFrom, addressTo));
            }
            catch (FaultException)
            {
                result.ErrorDto = new ErrorDto()
                {
                    Code = “100”,
                    Description = “Failure when retrieving mileage data from Bing Maps service.”
                };
            }
            catch (Exception e)
            {
                throw TMSException.Create(string.Format(“Unexpected failure happened when calling {0} mileage engine.”, mileageEngineCode), e);
            }
            return result;
        }

        private string retrieveAddress(XElement addressElement)
        {
            return formatAddress(
                addressElement.GetString(AddElementXmlConstants.Address),
                addressElement.GetString(AddElementXmlConstants.City),
                addressElement.GetString(AddElementXmlConstants.State),
                addressElement.GetString(AddElementXmlConstants.PostalCode),
                addressElement.GetString(AddElementXmlConstants.CountryRegion));
        }

        private string formatAddress(string address, string city, string stateProvince, string postalCode, string country)
        {
            string result = string.Format(“{0}, {1}, {2} {3}, {4}”, address, city, stateProvince, postalCode, country);

            return result;
        }
    }
}

 

 

To make this class work we must ensure that the correct Bing Maps service access key is assigned to the key variable in the RetrieveMiles method. For testing, we can hardcode the value, but this approach should not be used for the production environment. The access key should be treated as a protected resource and should be stored and sourced securely.

Enablement and testing

After deploying the solution to the server and restarting the AOS, the CustomTMSEngines is available under [server bin]\VSAssemblies. We can now create a mileage engine that uses our new engine code and enable it for rating. To do that, follow these steps:

1.       Click Transportation management > Setup > Engines > Mileage engines.

2.       Create a new engine using the following fields values:

a.       Mileage engine=BingMaps

b.      Name=Bing Maps Mileage Engine

c.       Engine assembly=CustomTMSEngines.dll

d.      CustomTMSEngines.BingMapsMileageEngine

 

We do not need to define engine metadata and data because our engine uses external sources.

The following screenshot shows the BingMaps engine with the settings we’ve just entered.

 We don’t have to define engine parameters because the current implementation does not require them.

Yes, the form should be empty, as shown in the screenshot above.

3.       Now it’s time to make this engine consumed by a rate engine that uses mileage when calculating rates. Click Transportation management > Setup > Engines > Rate engine.

4.       For this exercise we will modify a preconfigured rate engine so that it uses BingMaps mileage engine (we could also define a new rate engine based on the already defined engine type, associate it with our new mileage engine, and use them side-by-side). If we initialized our system using the Initialize base engine data button in Transportation management parameters, we find a record that uses following rate engine type:

Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine.

Select the engine, and then click the Parameters button. 

5.       We can now associate the MileageRateEngine with the BingMaps engine. To do this, set BingMaps as the value of the MileageEngineCode parameter, as shown in the following screenshot.

 

6.       Now let’s open the Rate route workbench and perform an ad-hoc rating, as shown in the following as on the screenshot.

 

7.       If we compare the results of the ad-hoc rating with the routing result from the Bing Maps web page, the result of 6.1 miles is the same as on the mileage response from TMS system.
 

Conclusion

It is fairly easy to build TMS engines that consume external services. This blog post has described a sample case in which we built a mileage engine that consumes mileage data from Bing Maps. Similar steps would be used to integrate with other mileage data providers. There are some products on the market that are specifically designed for transportation mileage calculation.

To wrap this up, I’d like to share a few tips with you if you decide to build your own engines that communicate with external systems:

·        Make sure to threat model your solutions, and ensure that service endpoint data and authentication data is secured.

·        Visual Studio unit tests are a good approach for testing the engine in isolation. If you go for X++ unit tests, restarting the AOS is required for every production code change.

·        The advantage of relying on external routing systems is that you don’t need to maintain address and routing data. Unfortunately, the disadvantage is that it can happen that some addresses recorded in AX may not be available in your external mileage system (for example, street numbers may be missing). You may consider writing a specific test or a job that iterates through all the transportation destinations and verifies that the mileage data can be retrieved. Otherwise you will discover those during operation.

 

CustomTMSEngines.zip

Comments (33)

  1. PoojaSaini says:

    Hi Pawel,

    I tried to create same engine Bing Map Mileage engine, i got a temporary key from bing maps.

    But i after Enabling the engine and clicking on Rate Route workbench –> Rate Shop

    i was not able to get the miles as shown above in your blog with the same address from and to, after processing i was able to get lines with the exceptions in the exception column, i am not able to add snapshot shots for you to view. But can you help if you understood my issue ?

  2. Pawel Kruk says:

    Hi Pooja,

    I assume that you first tried smoke-test your implementation with the same addresses as in this tutorial, right? I cannot guess what's happening on your environment. I would suggest to put a break point on following line in RetrieveDistance method:

    RouteResponse routeResponse = routeService.CalculateRoute(routeRequest);

    And see what happens. I would guess that this the likely place that is throwing exception. Please inspect the exception message.

    Pawel

  3. PoojaSaini says:

    Hi Pawel,

    Firstly —

    I tried to do some changes in the setup and i was able to get 6.12 Miles in the miles column. But as you have only one shipping Carrier as "TestMileage" i have 6 Shipping Carriers and if you can see there is an Exception tab on this form when i click on that exception it has different exceptions for each of the 6 shipping carrier

    Point 2 point  truck —  Has Rate Base Detail could not be found          

    Truck Carrier —           Carrier fuel index not found

    Parcel Carrier —         Could not find transit time

    Rail Carrier  —            Could not find transit time

    Flower moving —        Could not find transit time

    Zone 2 Zone truck —  Could not find transit time

    I need to do some setups to get rid of these exceptions ??

    Secondly —

    If i am putting some other addresses in From and To except the Microsoft ones, i am not getting any result in Miles it is 0.00

    and If i click on the exception Tab

    Error shows as "Failure when retrieving mileage data from Bing Maps Service" for all the Shipping Carriers

    This could be the key issue, that i need to have a paid subscription ??

  4. Pawel Kruk says:

    Hi Pooja,

    For your first part of the question – this is totally not related to the mileage system. You seem to be working with demo data, which is quite limited. My guess is that in your Rate route workbench you are trying to execute rate shopping based on origin and destination addresses that miss setup data in TMS system. For each of the carriers that didn't return result, find the "Rating profile" and verify that any "address" specific filter and date effectivity is correct on associated "transit time engine", rate master ("rate base assignment" and then related "rate base") and "carrier fuel index". If you are just interested in testing mileage, perhaps you want to disable the other carriers, transit time calculation and so on, not to pollute the picture.

    As for the second part of the question, you do not need paid subscription. The Bing maps trial key limits the number of requests and the lifetime of the key to 90 days. That should not limit the availability addresses that you use for mileage calculation.

    If you look at one of the last sentences in this blog post:

    "Unfortunately, the disadvantage is that it can happen that some addresses recorded in AX may not be available in your external mileage system"

    you may consider one of the two possibilities:

    1. You are trying to find address that cannot be found by Bing Maps (e.g. building and apartment numbers seem to be missing in some georgraphical locations).

    2. You are working in a geographical location, that uses different address formatting that we used in the code sample described in this tutorial (USA).

    One way or another, please use following steps to find out what's going on:

    1. Put a breakpoint in first line of following method

    public double RetrieveDistance(string addressFrom, string addressTo)

    2. Once you hit the breakpoint, take the addressFrom value and addressTo value, and try to find the addresses and the direction using BingMaps portal:

    http://www.bing.com/maps/

    3. If you fail to find the address, try to start from removing the building number. If it helps, you will start getting more results, but less accurate (I guess in most of the cases it will be a matter of a couple of hundred meters). The you can try to remove stateProvince from your request.

    If you find out what works for you, then you will need to apply correction of address formatting method, which is this one:

    private string formatAddress(string address, string city, string stateProvince, string postalCode, string country)

    A question: Can you tell me what country data you are using? Can you provide a sample address you are trying to work with?

    Pawel

  5. PoojaSaini says:

    I am using USMF company and i tested my addresses were not actual addresses and i checked with the new addresses, it is working now. Thank you for your help.

  6. PoojaSaini says:

    Hi Pawel,

    May i know how did you get the links for Bing map SOAP and REST service ?

  7. Pawel Kruk says:

    Hi Pooja,

    At the beginning of this blog post I put some links that explain details about the services. These are available on msdn:

    The following article provides more information about the Bing Maps SOAP services:

    msdn.microsoft.com/…/dd221354.aspx

    The following article provides more information about the Bing Maps REST services:

    msdn.microsoft.com/…/ff701713.aspx

    Please follow these for more info about the wsdl references and so on.

    Pawel

  8. Victor says:

    Hi; I'm trying to follow the tutorial by in the steep 5 it's not appearing the BingMapsEngine, I've tried it on the record with the Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine. value…… What can I do?

  9. Ari Smith says:

    Simply put you are wasting your time, Bing Maps is NOT an accepted transportation industry standard because it primarily geared for retail consumer use. Users who are engaged in commercial transportation and pay transport charges based on established truck routes, use one of the providers such as PC Miler 27. Tier I TMS software do not use Bing Maps.

  10. neikorz says:

    Hello,

    I've been trying to run the engine .. the code in Visual Studio apparently works well but when I enter the address data in the Tmsraterouteworkbench form as shown in the picture, if I debug and see the variable distance gives the value correctly, but when it is displayed in dynamics does not show anything, don't return any value in the grid ..

    On the other hand only works project code if I click the button rates in the form .. Show rates on the grid but the field of mileage with value 0 , however if I click the button route does nothing get fields to 0, the debugger does not start at the breakpoint of visual studio .. most likely I have something that might not have set well but have followed all the steps as the tutorial says but does not work

  11. Prabhu says:

    This is very good article about TMS engines, I tried to follow the whole process but when I clicked on the Rate Shop I was getting this error message below

    " Error RateShopBroker, TMSException"

    Please help me.

  12. prabhu.T says:

    I tried to follow the whole process but when I clicked on the Rate Shop I was getting this error message below

    " Error RateShopBroker, TMSException"

    Please help me.

  13. prabhu.T says:

    Hi PaweI,

    I hope u will understand my issue . i checked transportation error log and find the following error log,

    Exception at level 0

    Message: Error RateShopBroker, TMSException

    Source: Microsoft.Dynamics.Ax.Tms

    Stack trace:   at Microsoft.Dynamics.Ax.Tms.API.TmsService.ExecuteTransaction(String requestXml)

    Exception at level 1

    Message: Unable to cast object of type 'CustomTMSEngines.BingMapsMileageEngine' to type 'Microsoft.Dynamics.Ax.Tms.Bll.IRateEngine'.

    Thanks in advance.

  14. neikorz says:

    Hi,

    I have the same error.

    Error RateShopBroker, TMSException

  15. Pawel Kruk says:

    Hi Prabhu,

    Your message:

    Unable to cast object of type 'CustomTMSEngines.BingMapsMileageEngine' to type 'Microsoft.Dynamics.Ax.Tms.Bll.IRateEngine'.

    suggests that you have done a wrong setup in AX. It looks like you registered a Rating Engine based on 'CustomTMSEngines.BingMapsMileageEngine'.

    'CustomTMSEngines.BingMapsMileageEngine' is a mileage engine, which you set up in:

    Transportation management -> Setup -> Engines -> Mileage engine

    The responsibility of mileage engine is to answer: "how many miles there is from point A to point B?".

    Your actual Rate engine, which you setup here:

    Transportation management -> Setup -> Engines -> Rate engine

    Should remain Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine. You should only change the Parameters of this engine (as on the step 5 of ""Enablement and testing" in blog above) to assign the BingMaps engine to MileageEngineCode. This change will only ensure that your mileage-based rating engine will use a Bing mileage engine for retrieving rates.

    I hope it helps.

    Pawel

  16. Pawel Kruk says:

    Hi neikorz,

    Can you check the TMS error log:

    Transportation management -> Periodic -> Transportation system error log

    for more details?

    Pawel

  17. neikorz says:

    Hi Pawel, this is my error log:

    Exception at level 0

    Message: Error RateShopBroker, TMSException

    From: Microsoft.Dynamics.Ax.Tms

    Seguimiento de pila:    at Microsoft.Dynamics.Ax.Tms.API.TmsService.ExecuteTransaction(String requestXml)

    Exception at level 1

    Message: Unable to cast object of type 'Microsoft.Dynamics.Ax.Tms.Bll.P2PMileageRateEngine' to type 'Microsoft.Dynamics.Ax.Tms.Bll.ITransitTimeEngine'.

    From: Microsoft.Dynamics.Ax.Tms

    Seguimiento de pila:    at Microsoft.Dynamics.Ax.Tms.Bll.TransitTimeEngineFactory.CreateTransitTimeEngine(String transitTimeEngineCode)

      at Microsoft.Dynamics.Ax.Tms.Bll.TransitTimeHelper.RetrieveTransitTime(TransactionFacade transactionFacade, RatingDto ratingDto, XElement pickup, XElement dropOff)

      at Microsoft.Dynamics.Ax.Tms.Bll.TransitTimeHelper.RetrieveTransitTime(TransactionFacade transactionFacade, XElement segment, IList`1 ratingDtos)

      at Microsoft.Dynamics.Ax.Tms.Bll.RatingService.ProcessTransitTime(TransactionFacade transactionFacade, XElement segment, IList`1 ratingDtos)

      at Microsoft.Dynamics.Ax.Tms.Bll.RateShopBroker.RateEntity(TransactionFacade transactionFacade, XElement entity)

    Thanks in advance.

  18. prabhu t says:

    Hi Pawel,

    Thanks for reply. I have created customTMSEngine.dll and configure the following setup alone.

    I think I have missed some other setup. I have tested the custom dll it give perfect result. Only problem is while calling in ax through std it was throwing   the error " Error RateShopBroker, TMSException"

    1.Mileage Engine

    Created new mileage engine(BingMaps) and mapped assembly and engine type.

    2.Rate engine:

    I selected Mileage rate engine and clicked parameter Button. Change the mileage engine as (BingMaps)

    IS any other setup I have missed?

    Thanks in advance.

  19. Pawel Kruk says:

    Hi Neikorz,

    You are making similar mistake as Prabhu. Your setup seems messed up. For some reason you have a transit time engine with following type:

    Microsoft.Dynamics.Ax.Tms.Bll.P2PMileageRateEngine

    registered in your system. This is not a transit time engine, this is a rate engine, which you define here:

    Transportation management -> Setup -> Engines -> Rate engine

    Please remove your transit time engine and recreate the setup as a rate engine. I would suggest taking a second deployment or separate legal entity and hitting "initialize base engine data" button in the TMS parameters form. This will initialize engines in the right places. You should then see the list of transit time engines, rate engines and their parameters. You will then find inconsistencies with your setup.

    Pawel

  20. Pawel Kruk says:

    Hi Prabhu,

    Can you share the list of engines you have here on this form:

    Transportation management -> Setup -> Engines -> Rate engine

    Pawel

  21. neikorz says:

    Hi again Pawel,

    I initialized base engines and error is gone, but now does not work the code of the dll project. Only shows in the grid all results to 0, I debug attempt to see how works and the problem is that now fails to enter the dll . I set everything as explained in the example of this blog, I think the first engine mileage and then I add the parameter in the engine rate , there is no communication between AX and DLL. I don't know how to solve this issue because there is no error.

    Thanks in advance

  22. neikorz says:

    Can anyone tell me what I should set more than add the new BingMaps mileage engine and rate engine parameter to run the CustomTMSEngine???

    Thanks

  23. Pawel Kruk says:

    Neikorz,

    I think you need to double check following:

    1. that uses following rate engine:

    Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine

    2. The following rating engine

    Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine

    has BingMaps assigned to MileageEngineCode parameter

    3. Once you ensure, that you have that structure, use rate shop. If you are getting rate=0, check out the exceptions tab on Rate route workbench. It should then say whats going on.

    As for debugging, please refer to this blog:

    blogs.msdn.com/…/third-party-transportation-management-tms-engines-ups-fedex-progistics-pcmiler.aspx

    In one of the recent comments i wrote down a couple of tricks around debugging using Visual Studio tools for AX.

    Pawel

  24. Prabhu.T says:

    HI Pawel,

    1st I went Transportation mgt ->setup -> clicked Initialized base engine data so the following Rate engine lines created.

    1.LTL

    2.LTLFak

    3.Mileage  -> Clicked parameters Button  -> change Mileage engine code as Bingmaps(tat i was created)

    4.P2PContract

    5.P2PMileage

    6.P2PWeight

    7.Piece

    8.PostalZoneWeight

    9.VolumeSTCC

    Workaround:

    i am using P2P mileage engine but details will be fetching from using bing map service.

    whenever i click RateRoute button before i calculate distance using custombingmap.dll in ax and store from location, to location , distance in P2P (Mileage engine) details grid.

    Thank you so much for your continuous support.

    Regards,

    T.Prabu

  25. neikorz says:

    Hi Pawel,

    There is no Exception, no Error, not show us anything. I have reconfigured all again..

    I created the project step by step again in the visualStudio  , I deployed the proyect again  , I have ensured that the dll is in the vsAssemblies folder and in the  C# projects of AOT.. I dont know what else I have to look..

    It seems that  not detect any type of motor.  In the debugger, when I enter in the execute() method and start writing the XML with the WriteXml( ) method, this create the nodes and the whole structure until arrive to the commproxy.sendRequest() method, once trying to send the transaction does not send anything to visual studio, not  enter it in the tmsService.executeTransaction( ) method and dont go to our customTMSEngine code or Microsoft.Dynamics.Ax.Tms.dll.. After this continua to ReadXml( ) method to find again the empty tags and no shows us nothing, all grid values ​​of the form TMSRateRouteWorkBench are 0 or nulls.

    I have the Mileage Engine with this parameters:

    Mileage Engine : BingMaps

    Name: Bing Maps Mileage Engine

    Engine Assemblie: CustomTMSEngines.dll

    Engine Type: CustomTMSEngines.BingMapsMileageEngine

    And the Rate Engine i have modified the parameter MileageCodeEngine of Microsoft.Dynamics.Ax.Tms.Bll.MileageRateEngine to BingMaps

  26. neikorz says:

    Hi,

    The last  problem is solved, now got this error.

    Mileage detail could not be found

    The code Works nice but dont show the mileage in the grid. The rest of fields like rates yes and the operation to get the mileage in the XML its ok but i can retrieve it in the raterouteworkbench form grid.

    Here is my error log now:

    Exception at level 0

    Message: An unexpected error occured while performing the requested operation, TMSException

    Source: Microsoft.Dynamics.Ax.Tms

    Stack trace:   at Microsoft.Dynamics.Ax.Tms.Utility.DataLoadBootstrapper.ExecuteBaseData()

    Exception at level 1

    Message: Cannot create a record in Transit time field (TMSTransitTimeField). Transit time engine: PointToPoint, 1.

    The record already exists.

    Source: Microsoft.Dynamics.AX.ManagedInterop

    Stack trace:   at Microsoft.Dynamics.AX.ManagedInterop.Record.CallWithReturnType(String methodName, Type returnType, Object[] paramList)

      at Microsoft.Dynamics.AX.ManagedInterop.Record.CallWrapper(String strMethod, Boolean checkKeys, UInt16 dwSecKey, Byte accessType, Object[] pParamList)

      at Microsoft.Dynamics.AX.ManagedInterop.Record.Insert()

      at Microsoft.Dynamics.Ax.Tms.Data.AXDataRepository.SaveRecord[T](T record)

      at Microsoft.Dynamics.Ax.Tms.Data.AXDataRepository.SaveTransitTimeField(TMSTransitTimeField transitTimeField)

      at Microsoft.Dynamics.Ax.Tms.Data.TmsDataService.SaveTransitTimeField(TMSTransitTimeField transitTimeField)

      at Microsoft.Dynamics.Ax.Tms.Utility.TransitTimeEngineBS.InsertTransitTimeField(String transitTimeEngineCode, String name, Int32 sequence, TMSDataType dataType, NoYes mandatory, TMSLookupType lookupType)

      at Microsoft.Dynamics.Ax.Tms.Utility.TransitTimeEngineBS.InsertData()

      at Microsoft.Dynamics.Ax.Tms.Utility.DataLoadBootstrapper.ExecuteBaseData()

    Finally got the Miles but can't be retrieved in ax.

    Thanks so much for the patience

  27. Pawel Kruk says:

    Hi neikorz,

    I think you are getting this error "Cannot create a record in Transit time field (TMSTransitTimeField). Transit time engine: PointToPoint, "

    because you are trying to re-initialize base engine data that is already initialized.

    As for the "Mileage detail could not be found", please double check in the mileage engine, you find the detail that corresponds to the "from" and "to" address on your rate route workbench.

    Also, I assume that the list of fields on PointToPoint mileage engine has not been modified after you bootstrapped your system using "Initialize base engine data" button. It is not optional to change them.

  28. neikorz says:

    Hi Pawel,

    I think that the detail corresponds with the "from" and "to" address.. some errors in the engine tags on the XML.. Maybe help us to fix it..

    <?xml version="1.0" encoding="UTF-8" ?>

    – <Transaction>

     <Type>RATE</Type>

     <DataArea>usmf</DataArea>

     <DlvTerm />

     <CarrierCode />

     <CarrierServiceCode />

     <CarrierGroupCode />

     <InventSiteId />

     <InventLocationId />

     <ThirdPartyAccount />

     <ModeCode />

     <BaseCurrency />

    – <DefaultDimension>

     <BusinessUnit />

     <CostCenter />

     <Department />

     <ItemGroup />

     <Project />

     </DefaultDimension>

     <ModuleType>Cust</ModuleType>

    – <Accounts>

     <CustomerCode />

     <CustomerGroup />

     <CustomerInvoiceCode />

     <VendorCode />

     <VendorGroup />

     <VendorInvoiceCode />

     </Accounts>

    – <Request>

    – <ShippingEntity>

     <Type>ROUTE</Type>

    – <ShippingEntity>

     <Type>SHIPMENT</Type>

     <RouteGuideName />

     <RoutePlanName />

     <RouteConfigurationCode />

     <Sequence>0</Sequence>

     <Customer />

     <ResponsibleForPayment>Company</ResponsibleForPayment>

     <VendorCode />

     <VendorInvoiceCode />

    – <PickUp>

     <Address />

     <City>Redmond</City>

     <State>WA</State>

     <PostalCode>98052</PostalCode>

     <CountryRegion>USA</CountryRegion>

     <County>KING</County>

     <CountryRegionISOCode>US</CountryRegionISOCode>

     <PhoneNumber />

     <Residential>No</Residential>

     <ResponsibleForPayment>Company</ResponsibleForPayment>

     <VendorCode />

     <VendorInvoiceCode />

     </PickUp>

    – <DropOff>

     <Address />

     <City>Bellevue</City>

     <State>WA</State>

     <PostalCode>98004</PostalCode>

     <CountryRegion>USA</CountryRegion>

     <County>KING</County>

     <CountryRegionISOCode>US</CountryRegionISOCode>

     <PhoneNumber />

     <Residential>No</Residential>

     <ResponsibleForPayment>Company</ResponsibleForPayment>

     <VendorCode />

     <VendorInvoiceCode />

    – <Mileage>

     <MileageEngineCode>BingMaps</MileageEngineCode>

     <Miles>8.24497434979718</Miles>

     </Mileage>

    – <Mileage>

     <MileageEngineCode>P2P</MileageEngineCode>

    – <Errors>

    – <Error>

     <ErrorCode>General0002</ErrorCode>

     <ErrorDesc>Mileage detail could not be found</ErrorDesc>

     </Error>

     </Errors>

     </Mileage>

     <ZoneMasters />

     </DropOff>

     <Weight>0.00</Weight>

     <Volume>0.00</Volume>

     <STCC />

     <DeliveryDate />

     <ShipDate />

     <DlvTerm />

     <EquipmentCode />

     <Value>0.00</Value>

  29. neikorz says:

    – <RatingEntities>

    – <RatingEntity>

     <CarrierCode>Flower moving</CarrierCode>

     <CarrierServiceCode>STD</CarrierServiceCode>

     <ModeCode>Ground</ModeCode>

     <MethodCode>Ground</MethodCode>

     <ApportionmentEngine>AppWeight</ApportionmentEngine>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

    – <Errors>

    – <Error>

     <ErrorCode>Rate0008</ErrorCode>

     <ErrorDesc>Rate base assignment cannot be found</ErrorDesc>

     </Error>

     </Errors>

     </RatingEntity>

    – <RatingEntity>

     <CarrierCode>ParcelCarrier</CarrierCode>

     <CarrierServiceCode>STD</CarrierServiceCode>

     <ModeCode>TL</ModeCode>

     <MethodCode>Ground</MethodCode>

     <ApportionmentEngine>AppWeight</ApportionmentEngine>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

     <RateBase>ParcelRateBase</RateBase>

     <TotalRate>85.96</TotalRate>

    – <Rate>

     <RateType>0</RateType>

     <TotalRate>85.96</TotalRate>

     <Units>8.24497434979718</Units>

     <UnitRate>8</UnitRate>

     <Code>ParcelRateBase</Code>

     <BillingGroupID>Freight</BillingGroupID>

     <ExternalCode>PCG0001</ExternalCode>

     <CurrencyCode>USD</CurrencyCode>

     <ObjectID />

     <CustomerRate>85.96</CustomerRate>

     <ShipperRate>85.96</ShipperRate>

     </Rate>

     <CurrencyCode>USD</CurrencyCode>

    – <TransitTime>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

    – <Errors>

    – <Error>

     <ErrorCode>General0004</ErrorCode>

     <ErrorDesc>Could not find transit time</ErrorDesc>

     </Error>

     </Errors>

     </TransitTime>

     </RatingEntity>

    – <RatingEntity>

     <CarrierCode>Point 2 Point Truck</CarrierCode>

     <CarrierServiceCode>STD</CarrierServiceCode>

     <ModeCode>Ground</ModeCode>

     <MethodCode>Ground</MethodCode>

     <ApportionmentEngine>AppWeight</ApportionmentEngine>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

    – <Errors>

    – <Error>

     <ErrorCode>General0002</ErrorCode>

     <ErrorDesc>Mileage detail could not be found</ErrorDesc>

     </Error>

     </Errors>

     </RatingEntity>

  30. neikorz says:

    – <RatingEntity>

     <CarrierCode>RailCarrier</CarrierCode>

     <CarrierServiceCode>Rail</CarrierServiceCode>

     <ModeCode>Rail</ModeCode>

     <MethodCode>Ground</MethodCode>

     <ApportionmentEngine>AppWeight</ApportionmentEngine>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

     <RateBase>RailBase</RateBase>

     <TotalRate>170.08</TotalRate>

    – <Rate>

     <RateType>0</RateType>

     <TotalRate>170.08</TotalRate>

     <Units>8.24497434979718</Units>

     <UnitRate>8.5</UnitRate>

     <Code>RailBase</Code>

     <BillingGroupID>Freight</BillingGroupID>

     <ExternalCode />

     <CurrencyCode>USD</CurrencyCode>

     <ObjectID />

     <CustomerRate>170.08</CustomerRate>

     <ShipperRate>170.08</ShipperRate>

     </Rate>

     <CurrencyCode>USD</CurrencyCode>

    – <TransitTime>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

    – <Errors>

    – <Error>

     <ErrorCode>General0004</ErrorCode>

     <ErrorDesc>Could not find transit time</ErrorDesc>

     </Error>

     </Errors>

     </TransitTime>

     </RatingEntity>

    – <RatingEntity>

     <CarrierCode>TruckCarrier</CarrierCode>

     <CarrierServiceCode>Truck</CarrierServiceCode>

     <ModeCode>TL</ModeCode>

     <MethodCode>Ground</MethodCode>

     <ApportionmentEngine>AppWeight</ApportionmentEngine>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

     <RateBase>TruckRateBase</RateBase>

     <TotalRate>70.08</TotalRate>

    – <Rate>

     <RateType>0</RateType>

     <TotalRate>70.08</TotalRate>

     <Units>8.24497434979718</Units>

     <UnitRate>8.5</UnitRate>

     <Code>TruckRateBase</Code>

     <BillingGroupID>Freight</BillingGroupID>

     <ExternalCode />

     <CurrencyCode>USD</CurrencyCode>

     <ObjectID />

     <CustomerRate>70.08</CustomerRate>

     <ShipperRate>70.08</ShipperRate>

     </Rate>

     <CurrencyCode>USD</CurrencyCode>

    – <TransitTime>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

    – <Errors>

    – <Error>

     <ErrorCode>General0004</ErrorCode>

     <ErrorDesc>Could not find transit time</ErrorDesc>

     </Error>

     </Errors>

     </TransitTime>

     </RatingEntity>

    – <RatingEntity>

     <CarrierCode>Zone 2 Zone Truck</CarrierCode>

     <CarrierServiceCode>STD</CarrierServiceCode>

     <ModeCode>Ground</ModeCode>

     <MethodCode>Ground</MethodCode>

     <ApportionmentEngine>AppWeight</ApportionmentEngine>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

    – <ZoneMasters>

    – <ZoneMaster>

     <ZoneMasterCode>Postal</ZoneMasterCode>

    – <Errors>

    – <Error>

     <ErrorCode>General0005</ErrorCode>

     <ErrorDesc>Zone master detail could not be found</ErrorDesc>

     </Error>

     </Errors>

     </ZoneMaster>

     </ZoneMasters>

    – <Errors>

    – <Error>

     <ErrorCode>General0005</ErrorCode>

     <ErrorDesc>Zone master detail could not be found</ErrorDesc>

     </Error>

     </Errors>

    – <TransitTime>

     <TransitTimeEngineCode>PointToPoint</TransitTimeEngineCode>

    – <Errors>

    – <Error>

     <ErrorCode>General0004</ErrorCode>

     <ErrorDesc>Could not find transit time</ErrorDesc>

     </Error>

     </Errors>

     </TransitTime>

     </RatingEntity>

     </RatingEntities>

     </ShippingEntity>

     </ShippingEntity>

     </Request>

     </Transaction>

  31. neikorz says:

    Finally solved

  32. Rishi says:

    Hi Pawel,

    Could you please guide.

    I have set this whole procedure and try to use UPS rate engine firstly but I am facing couple  of issues with this-

    – getting this exception "TmsEngineSetup " with error message that "dll found and loaded but engine type cannot be found".

    – Not able to hit the break point in engine class's initialize method.

    Regards,

    Rishi

  33. Vaidas Karosas says:

    Hi Rishi,

    Sorry for the late answer.

    This blog post discusses a custom mileage engine implementation. There is another blog post on third-party TMS engines, e.g. UPS rating: blogs.msdn.com/…/third-party-transportation-management-tms-engines-ups-fedex-progistics-pcmiler.aspx.

    Regarding the issues you are seeing, it seems like they have the same root cause – incorrect rate engine configuration. Rate engines are configured in Transportation management/Setup/Engines/Rate engine. The error message indicates that an incorrect Engine type has been specified. Engine type should be the name of your class implementing the IRateEngine interface. Since the engine class cannot be loaded, it also is not executed, and no breakpoint in it is hit. Make sure that your rate engine class is in the specified assembly, and that the assembly is loaded. In the comment section of the above-mentioned blog post, there is a small discussion about assembly loading; perhaps you'll find something useful.

    Vaidas

Skip to main content