This is the second part of a blog series on the Warehouse mobile device portal (WMDP) introduced in Microsoft Dynamics AX 2013 R3. The first part can be found here. The mobile device portal is designed to provide warehouse workers with an interface into the advanced warehousing system of Microsoft Dynamics AX and is intended to be used on a wide variety of devices within the warehouse. This post is designed to help you understand the overall architecture of the mobile device portal and how the existing code operates to generate the mobile device pages.
WMDP – Architecture
To understand how the mobile portal ultimately renders the warehouse work on the mobile device, it is important to take a step back and see how the overall data flow operates and all of the major components in the solution. The following picture shows the high level objects that constitute the warehouse mobile portal architecture.
The far left represents the end user device, which displays the final HTML in a browser or emulator. As mentioned in the previous blog post, the mobile portal is designed to render across a wide variety of devices and form-factors, and we have tried to ensure the generated HTML is widely compatible across browsers and devices.
IIS, or Internet Information Services, executes on the server and acts as the Web server for the mobile device portal. Within IIS the warehouse mobile device portal executes as an ASP.NET web application. This application accepts the HTTP communications from end-user browsers and translates these into web service calls to the AOS endpoint – ultimately resulting in new webpages being generated for the end-user, based on their current workflow state and data entered and/or returned from the Application object server (AOS).
The AOS hosts the WHSMobileDeviceService endpoint as an Application Integration Service endpoint. This is the entrypoint into AX for all advanced warehouse functionality exposed by the mobile portal. You can see the details of the WHSMobileDeviceService in the AOT under the Services node, and you can configure the exposed endpoint within AX by navigating to System administration -> Setup -> Services and Application Integration Framework -> Inbound ports. The configuration for this endpoint on my machine can be seen below:
WMDP – Data Flow
Let's walk through a typical data exchange from the client-side device to the AOS and back. This will illustrate how each of these components operate within the framework and what they contribute to the overall solution.
Step 1 – Client-side HTTP Form Post
All communication is initiated from the client-side via standard HTTP operations. The initial webpage is retrieved via an HTTP GET operation – providing the starting point for the mobile device. After this all interactions with the server-side portal are conducted with HTTP Form POST commands. Remember that one of the design goals for this portal was to ensure we could support low-power and low-capability devices. As such the portal is not designed as a fancy JavaScript single-page app – instead we rely on standards-based, tried-and-true HTML4/HTTP functionality that should be available to most devices with an Internet browser.
The following are two examples of this form data being posted from the client-side browser. Note that the username/password fields are not encrypted in transit – which is why we recommend the mobile portal be exposed only on a trusted private network or to ensure secure transport protocols are implemented between the client-side device and the server (i.e. HTTPS). You will also note the various data fields are passed to the server as form/url-encoded data as well as the session token used to determine the user authentication and authorization details.
ClickedButton=&IsMenu=False&SessionGuid=ZZZ&__RequestVerificationToken=XXX&UserId=42&Password=1
ClickedButton=&IsMenu=False&SessionGuid=ZZZ&__RequestVerificationToken=XXX&PONum=123&Qty=1&UOM=
ClickedButton=&IsMenu=False&SessionGuid=ZZZ&__RequestVerificationToken=XXX&PONum=123&Qty=1&UOM=
Step 2 – WMDP Website Processing
Inside the WMDP website several validations occur on the incoming data, including determining if the mobile user is valid and currently still logged in. The incoming data is validated and the data is combined with an XML structure of the current workflow UI to create a Control XML structure. This XML is used to communicate with the WHSMobileDevicesService AIF endpoint. A sample of this, containing the controls constructed from a login submission request, would look like the following:
Step 3 – WHSMobileDevicesService Integration
The XML created by the mobile portal is now submitted to the endpoint exposed in the AOS. The address of this endpoint is defined in the web.config file of the mobile portal website; by default this is assumed to be on the same machine as the mobile portal – but this can be reconfigured for a multi-box deployment.
Step 4 – WHSMobileDevicesService Processing
The final step in the inbound chain, this is where the incoming request is processed by the AX Work framework and the next step in the workflow is generated and returned to the caller. This is the set of X++ classes we will dive into later in this post – and this is where it is possible for you to inject your own code into the system to represent custom workflows and operations you want to enable in your warehouse. It is also possible to customize the workflows shipped as part of AX R3/CU8 – as you will see later.
Step 5 – Control XML Returned
Ultimately all of the processing that occurs within the AOS will result in a package of XML being returned to the caller. This will contain the structure and content of the controls to ultimately display in the generated HTML – including any error or validation error messages that need to be displayed to the user.
The following XML is what might be returned from the AOS when the user is conducting a PO Receiving workflow but has entered an incorrect PO number. You can see the error label with the information to inform the user, and you can see all of the other controls that make up the structure of the user interface. You will also note that now that we have authenticated this user we have an additional node in the XML containing the session GUID to track the user authentication information.
Step 6 – WMDP Processing
The WMDP website uses the Control XML returned from the WHSMobileDevicesService endpoint to construct the final HTML that will be returned to the client device. This HTML is constructed based on the controls defined in the XML, as well as the mobile settings view page discussed in the previous blog post. The goal is ultimately to have the correct HTML generated for the client device/user for the specific workflow step they are currently executing.
Step 7 – HTML Returned
The final step in the process is returning the constructed HTML back to the client device. As discussed above, this HTML is designed to be widely compatible and standard-based back to HTML4, such that any device with a browser should be able to operate within the warehouse portal.
The basic format of the constructed HTML is created by the display settings view, which controls the layout and construction of the HTML structure. The default view creates an HTML table and populates the rows of the table with the returned controls.
WMDP – AOT Classes
The above walkthrough gives us a better idea of the overall data flow into and out of the mobile portal. If we go back and expand "Step 4" a bit – we will now cover what exactly happens within the AOS when a request comes in from a mobile device and how that is translated into a result to the user.
Recall that the endpoint the WMDP uses to communicate with the AOS is the WHSMobileDevicesService. This exposes five operations, but the primary one we will deal with in detail in the getNextFormHandheld. This is the operation invoked by the WMDP to determine the next step in the current workflow for the user.
The high-level flow of classes involved in constructing the response to the user can be seen below. We will walk through this tree and discuss what each of the various components are doing along the way. All of these classes can be found in a standard Microsoft Dynamics AX 2012 R3 deployment if you want to follow along.
At the highest level – the WHSMobileDevicesService service is responsible for exposing the operation endpoints to external processes. As we described above, the getNextFormHandHeld operation consumes and returns XML data. This service is backed by the WHSMobileDevicesServiceFacade class which implements the operation methods. In turn, this class utilizes the WHSWorkExecuteDisplay:: getNextFormHandheld method to start the actual logic of generating the return XML.
I consider the WHSWorkExecuteDisplay::getNextFormHandheld method as the true entrypoint to the WMDP Work Framework. This is where I put a breakpoint when debugging and want to see exactly what is entering the system from the client and what we are returning from the various Work X++ classes. The method itself is very straightforward:
public static str getNextFormHandHeld(str _xml)
{
container con;
con = WHSWorkExecuteDisplay::readXML(_xml);
return WHSWorkExecuteDisplay::getNextFormXML(con);
}
The logic is very simple – the incoming XML is first read and a container is constructed. This is then used when building the return XML.
The WHSWorkExecuteDisplay::readXML method converts the incoming Control XML into a semi-structured container format. This container is composed of 3+ containers – each of which are used throughout the processing and rendering process. I have tried to capture a high-level overview of the container below.
WHS container (con) | ||
Index 1 #StateInfo | State Info container | |
Index 1
| WHSWorkExecuteMode enumeration | |
Index 2
| State Step | |
Index 2 #PassthroughInfo | PassthroughInfo container | <WHSRFPassthrough Map object> |
Index 1
| Map identifier | |
Index 2
| Type of Keys stored in this map (String) | |
Index 3
| Type of Values stored in this map (String) | |
Index 4
| Number of values in the Map | |
Index n
| Key Name | |
Index n+1
| Value | |
Index 3+ #ControlsStart | Control Container | |
Index 1
| Control Type | |
Index 2
| Control Name | |
Index 3
| Control Label | |
Index 4
| Newline Indicator | |
Index 5
| Control Data | |
Index 6
| Control Data Type | |
Index 7
| Control Data Length | |
Index 8
| Error Indicator | |
Index 9
| Default Button Indicator | |
Index 10
| Selected Indicator | |
Index 11
| Control Color |
In the WHSWorkExecuteDisplay::getNextFormState we have code that consumes this container, primarily the StateInfo container, in order to figure out the correct class to load in order to process the user request.
You will note the mode and step variables are extracted out of the container structure using the various macros defined either in the base class WHSWorkExecuteDisplay or in the global #WHSRF macro definition.
After the validation of the user session information, the critical step in this code is the call to the WHSWorkExecuteDisplay::construct method. This is a factory method that accepts the current mode we are operating under and creates the appropriate WHSWorkExecuteDisplay derived class to handle the rest of user interface creation and workflow processing. Here is a snippet of this code:
If you were to define your own processing class you would need to hook some code into this method to ensure the new class is created when the framework requests that new WHSWorkExecuteMode class.
Once the correct WHSWorkExecuteDisplay* class has been constructed the framework will invoke the displayForm method on this class – passing in the container and the specific button clicked in the UI (if any). This method is the central location to define the state machine of the various workflows. The current state the user is executing is located in the step variable, and this can be updated to reflect movement in the state machine model. In addition, the WHSRFPassthrough Map object discussed above is used to pass data between the various classes/methods involved in creating the final output.
Here is an example of the displayForm method. It is a very common pattern to have the central switch statement on the step member variable – with each case representing a different state in the state machine this class is representing.
No comments:
Post a Comment