What you'll be learning about:
Images and integration messages
The workflow that is driven by the Driver Task List dashboard form generates a lot more information than mere status updates to the delivery tasks. The driver can optionally add a comment on both the Load Delivery and the Complete Delivery hidden forms; even more importantly, he or she can take one or more photos of the cargo. What's more, the signature that both of these hidden forms require are also submitted as images to the Mobilengine Cloud. A pattern seems to be emerging: your integration solution can't be complete without the ability to transfer images through the wire.
Images and other media deserve their own section in this integration tutorial because binary data is notoriously tricky to transfer over XML. Alas, SOAP-type protocols (such as the WDX) cannot transfer anything but XML messages, so Mobilengine integration transfers images and other media over HTTP, as a complement to the SOAP transfer.
All that gets transferred in the integration messages are the unique identifiers of the image files in the Mobilengine Cloud. The web service at the other end can then use the identifiers to retrieve the images from the Cloud. There is a dedicated bin in the Cloud to expose user-submitted images to integration solution web services through an SSL-secured connection.
So, if images or other binary data needs to be transferred in your integration solution, the transfer becomes a two-step process: first the integration messages are pushed from the Cloud to the web service, same as with any old outbound integration solution, but then the direction of the transfer switches, and the web service sends an HTTP request to the Mobilengine server to download the images themselves.
Enhancing your working outbound integration solution to handle images will require updating
the solution all across the board: you'll need to modify dacsem
, revise
workflow scripts, set up a dedicated endpoint on the Cloud, and remodel the web service
implementation to request and then process the images identified in the
dacs
messages.
Include image IDs in the message schema
For the workflow scripts to be able to include the unique identifier
(mediaid
in Mobilengine terminology) for each transferred image, and the
web service to query the image files based on the identifiers, the integration messages must
contain these IDs in the first place.
Open the original statusUpdate.dacsem
, and add the photos and the
signature, which is also submitted as an image, as XML elements to the existing
dacs
structure:
schema statusUpdate meta 'rockyJupiter:statusUpdate' namespace "http://schemas.rockyjupiter.com/statusUpdate" statusUpdate: element { assignmentId: attribute of type string; driver: element { userId: attribute of type string; name: content of type string without whitespace; } oldStatus: element of type string without whitespace values "Assigned" "Confirmed" "Loaded"; newStatus: element of type string without whitespace values "Assigned" "Confirmed" "Cancelled" "Completed" "Loaded"; changedAt: element of type dtu(dtf MM "/" dd "/" yyyy " " HH ":" mm ":" ss);photos: optional wrapped list of element photo of type string without whitespace; signature: optional element of type string without whitespace;
}
The following considerations resulted in the schema above:
-
You need to make the new
photos
element a list, because the allowmultishoot attribute of the photo controls in the Load cargo and Complete delivery forms lets the drivers submit more than one photo. -
In the mobile form language,
mediaid
s have theguid
data type, but you have to mapguid
s to strings for integration messages to grok them. -
You want to have the three workflow scripts all work from a single
dacsem
schema, but only two of the triggering forms (load
andunload
, but nottaskList
) include a signature image, so you need to make thesignature
element optional as well.
Now that you've defined new elements in the integration messages, it's time to move on to the workflow scripts that produce them, and populate the schema.
Update the server-side scripts
Since you haven't yet accessed form-submitted images in a workflow script before, it's
worth looking at the scripts that are supposed to assemble and send these new
dacs
in detail.
Remember, your scripts are associated with the taskList
,
load
and unload
mobile forms.
You want to take the unique identifiers of all the image files in the
loadPhoto
and unloadPhoto
photo controls, and stick them
into the photos
list in the dacs
.
The driver may have taken and submitted zero or more photos, but photos taken with a mobile
form photo
control are always submitted to the Mobilengine cloud as a list,
even if there is only a single photo. The same goes for the signature image, since the
signature control is a special photo
control. There is, however, never more
than exactly one signature image submitted with a
form.
server program load_server for form load using reftab assignments; using reftab drivers; //use the dacsem schema name using dacs statusUpdate; { var confirmedTask = db.assignments.Read ({usr:form.user.name, status:"Confirmed"}).Single(); var dacsUpdate = messages.statusUpdate.New(); var statusUpdate = dacsUpdate.statusUpdate; statusUpdate.assignmentId = confirmedTask.assignment_id; var driver = db.drivers.Read({userid:confirmedTask.usr}).Single(); statusUpdate.driver.userId = driver.userid; statusUpdate.driver.name = driver.name; statusUpdate.oldStatus = confirmedTask.status; statusUpdate.changedAt = dtu.Now();statusUpdate.photosNew(); foreach (var photo in form.root.loadDetails.Single().loadPhoto) { statusUpdate.photos.Add(photo.value.mediaid.ToStringN()); } statusUpdate.signature = form.root.Signature.Single().value.mediaid.ToStringN();
if(form.controlSubmit == form.root.loadCancel.load) { db.assignments.Update(confirmedTask, {status:"Loaded", load_type:form.root.loadDetails.Single().loadType.value, comments:form.root.loadDetails.Single().comments.value}); statusUpdate.newStatus = "Loaded"; } else { db.assignments.Update(confirmedTask, {status:"Cancelled", comments:form.root.loadDetails.Single().comments.value}); statusUpdate.newStatus = "Cancelled"; } dacsUpdate.Send(); }
Since
the photos
list is a complex optional element as defined by the message
schema, you first need to create a new instance of it in the script (line 18).
In the foreach
statement on lines 19-22, you make the script iterate
through the list of photos in the loadPhoto
control, and append the
mediaid
property of each one to the photos
element list
of the statusUpdate
dacs
.
mediaid
s have the guid
data type, but since there is no
such type available for integration messages, you need to call the
ToStringN()
method to convert the IDs into strings.
Even though it's technically a photo
, and therefore contains a list, the
signature control always submits exactly one child element. Because of this, when you make
the script populate the signature
element in the resulting integration
message, you lose the foreach
statement. Because it has no child elements
or attributes, you don't need to create a new instance either, even though it's declared as
optional in the dacsem
.
For the workflow script that handles the submission of Complete
Delivery forms, you pretty much need the same modifications. Go ahead and
copy/paste the modified part of the previous script, only make sure that this time, you pick
the photos from the unloadPhoto
control of the unload
form.
server program unload_server for form unload using reftab assignments; using reftab drivers; //use the dacsem schema name using dacs statusUpdate; { var loadedTask = db.assignments.Read({usr:form.user.name, status:"Loaded"}).Single(); db.assignments.Update(loadedTask,{status:"Completed"}); var dacsUpdate = messages.statusUpdate.New(); var statusUpdate = dacsUpdate.statusUpdate; statusUpdate.assignmentId = loadedTask.assignment_id; var driver = db.drivers.Read({userid:loadedTask.usr}).Single(); statusUpdate.driver.userId = driver.userid; statusUpdate.driver.name = driver.name; statusUpdate.oldStatus = loadedTask.status; statusUpdate.newStatus = "Completed"; statusUpdate.changedAt = dtu.Now();statusUpdate.photosNew(); foreach (var photo in form.root.loadDetails.Single().unloadPhoto) { statusUpdate.photos.Add(photo.value.mediaid.ToStringN()); } statusUpdate.signature = form.root.Signature.Single().value.mediaid.ToStringN();
dacsUpdate.Send(); }
You've got yourself a message schema that now accepts the unique identifiers that are generated for each image file submitted with the Driver Task List form, and assembly instructions that build and send schema-conformant integration messages.
You haven't touched the wire itself, so the new-form integration messages will arrive at the web service just fine. However, nothing but a bunch of string identifiers are tansferred at this point, which the receiving web service will ignore anyway.
You need to set up an endpoint for the web service to access the Mobilengine Cloud, and make the service send an HTTP request to the endpoint to fetch the images with the identifiers in the integration messages.
Expose the images through an inbound endpoint
An image endpoint is a special type of endpoint on the Mobilengine Cloud. When you go to the Dev console → Integration screen on the Backoffice site to create one, you'll see that the name is all that you can configure. This is because image endpoints all have the same URL, and each one has a unique and non-editable API key generated.
Figure 193. On the Backoffice site, there's a separate link to create an image endpoint on the Dev console → Integration screen
You won't need the name of the endpoint in the integration solution, so feel free to
call it anything descriptive or memorable. You'll definitely need the API key, though: send
it (preferebly encrypted) to the system engineer on the other end of the integration
wire.
Figure 194. You can name your image endpoint anything you like, but a unique API key will be generated for you
Click Ok and fire up Visual Studio to tweak the web service
implementation.
Make the web service request and process the images
As soon as the integration messages arrive at the web service, the roles are reversed briefly, and the WDX-compliant web service temporarily becomes the client, pulling images from the Mobilengine Cloud.
To download a photo or signature image from off the Mobilengine Cloud, a web service will need to send an HTTP GET request to the following web address:
https://tutorial.mobilengine.com/Services/Wdx/Rdtbin/<id>?key=<API key>
The
requested image's mediaid
identifies the requested resource, and the API
key authenticates the client web service.
Open the project in Visual Studio, and modify the WdxService.cs
file to
make the service download the photos and signature images from the
Cloud.
Download the zipped project files
using System;using System.IO; using System.Net;
using System.ServiceModel; namespace RockyJupiter.Mobilengine.Service { //Implement the service interface class WdxService: IWdx { private static string LastDacsid; //Key used to authenticate Mobilengine to the web service when sending data //This key can be specified on the Mobilengine Developer Console private static string keyAstroOut = "Very secret key"; //Key used to authenticate RockyJupiter to Mobilengine when downloading images //This key is generated by Mobilengineprivate static string keyImageDownload = "b1bcf63f214b4b8a8da685c1bf3c27fa"; private static string imageDownloadBaseUrl = "https://tutorial.mobilengine.com/Services/Wdx/Rdtbin/";
public void EnqueueDacs(Dacs dacs) { //Return EnqueDacsFail on key mismatch //by throwing the appropriate FaultException if (dacs.Key != keyAstroOut) { Console.WriteLine("Authentication failed for key {0}", dacs.Key); throw new FaultException<EnqueueDacsFail>(new EnqueueDacsFail { dacsid = dacs.dacsid, Message = "Dacs authentication failed" }); } //Ignore the message if it is a duplicate if (dacs.dacsid == LastDacsid) { Console.WriteLine("Dacs {0} has already been processed, ignoring."); return; } //Store the package ID of the last received message LastDacsid = dacs.dacsid; //Write the contents of the incoming message to the console Console.WriteLine("Processing dacs {0}:", dacs.dacsid); var statusUpdate = dacs.Content.Item; Console.WriteLine( "At {0} driver {1} changed the status of assignment {2} from {3} to {4}.", statusUpdate.changedAt, statusUpdate.driver.Value, statusUpdate.assignmentId, statusUpdate.oldStatus, statusUpdate.newStatus); //Download photos if anyif (dacs.Content.Item.photos != null) { foreach (var photo in dacs.Content.Item.photos) { DownloadImage(photo); } }
//Download signature image there's oneif (dacs.Content.Item.signature != null) { DownloadImage(dacs.Content.Item.signature); }
}private void DownloadImage(string mediaid) { var webClient = new WebClient(); webClient.BaseAddress = imageDownloadBaseUrl; //Calculate output image path based on the media id var imagePath = Path.GetFullPath(mediaid + ".jpg"); //Download the image webClient.DownloadFile(string.Format("{0}?key={1}", mediaid, keyImageDownload), imagePath); Console.WriteLine("Image downloaded to {0}.", imagePath); }
} }
The code uses the specified web address, the mediaid
s, and the API
authentication key to download and save image files from the Mobilengine Cloud. It then
writes a report of the process to the console.
Test image transfer across the solution
The proof of the pudding is in the eating: using the self-hosting that you set up to test the basic version of the integration solution, try transferring images this time.
-
Open the Driver Task List dashboard form on your mobile device, and confirm a delivery with the Confirm
submitbutton
. Tap the Load cargosubmitbutton
when it appears to open the Load Delivery form. Take a few photos, and sign the form, then tap the Load Cargo button to move the workflow along.When the Deliver cargo
submitbutton
appears in the task list, tap it. Use the Complete Delivery form that opens to take a few more pictures, add a signature, and submit the form. -
When you tap the
submitbutton
controls in ther various forms, theconfirm_server
,load_server
, andunload_server
workflow scripts go to work on your form submissions, put together threedacs
messages, and push them through to the web service. -
When the integration messages reach the web service, it processes them and prints their contents to the console. The console output now includes the path to the mobile-submitted images that the web service downloaded from the Mobilengine Cloud.
Processing dacs 2bb8d5c666a24e85979b16d82d8b1cdb: At 03/12/2015 15:33:44.549+02:00 driver Petar Hoyt changed the status of assignment 1c723ded-6d25-4212-bcbf-a92a731b0211 from Assigned to Confirmed. Processing ca0359ea33cf48f8b654010ef25a4655: At 03/12/2015 15:34:21.249+02:00 driver Petar Hoyt changed the status of assignment 1c723ded-6d25-4212-bcbf-a92a731b0211 from Confirmed to Loaded. Image downloaded to c:\RockyJupiter\5JXQVw0zhYC9cM.jpg. Image downloaded to Ff_yt-JWN1gzRM.jpg. Image downloaded to hz7qPURUjjGmZM.jpg. Processing ba146be3318647cdabeef6d9f237fa27: At 03/12/2015 15:35:21.012+02:00 driver Petar Hoyt changed the status of assignment 1c723ded-6d25-4212-bcbf-a92a731b0211 from Loaded to Completed. Image downloaded to JMteA1yQMGUXPM.jpg. Image downloaded to au4I0yYffGY5RM.jpg.
-
Just to be on the safe side, check that the images have been downloaded and are indeed sitting on the disk where the web service says. Go ahead and open one of the transferred images at random.
What a coincidence. It's Mobilengine anywhere you look these days.
If you've come this far, your Mobilengine solution development skills can be seen from space.
Go and create some workflow solutions for public good and private gain.