December 28, 2016

Improving Assets and Locations drilldown dialog

The Assets/Locations drilldown dialog is one of the most longstanding and criticized features of Maximo.

This dialog is almost unchanged from release 6.1 and definitely lacks some important capabilities to provide a unified view of assets and location hierarchy. However, we can make few minor improvements to the standard dialog to make it slightly better.

Increase size of the dialog

I think the most usability improvement is to increase the size of the dialog allowing to see more elements in the drilldown. The default size is definitely too small for the typical modern screen resolutions.
Unfortunately there is no system setting for this and we have to tweak the dialog definition in the library.
Open the Application Designer and select the Export System XML action from the menu. Click on the small arrow for the LIBRARY row to export the file. Open the XML file with a good text editor and search for psdi.webclient.beans.common.DrilldownTreeBean.
There are two occurrences of this string in the file inside the 'drilldown' dialog definition. One is for the Locations tab and one is for Assets tab.
Change the height attribute increasing it from the default 150 to 350 or 400.

<tree id="locations_tree" beanclass="psdi.webclient.beans.common.DrilldownTreeBean"
width="500" height="350" checkfornewnodes="true"
ignorestructurechange="false" orderby="location asc" relationship="LOCATIONS" selectfirstnode="true">

Now in the Application Designer app, click on the Import Application Definition to import the modified LIBRARY.xml file.

You can see from the screenshot below how much easier it is to get a grasp of the location hierarchy with the new larger dialog.

Increase child records limit

Another small improvement that can be made is to improve the default limit of child records that can be displayed. The default value is set in the control-registry.xml file and is typically set to 50 which is too low.
To override the default value, just add the maxchildren attribute and specify a larger value like 500. This is how the locations_tree definition should look like.

<tree id="locations_tree" beanclass="psdi.webclient.beans.common.DrilldownTreeBean"
width="500" height="350" maxchildren="500" checkfornewnodes="true"
ignorestructurechange="false" orderby="location asc" relationship="LOCATIONS" selectfirstnode="true">

For more information refer to this post.

November 29, 2016

Tracking Changes using Audit feature

A common requirement in Maximo is to track changes made by users on a specific object (database table). In Maximo terms this is called 'Audit Tracking'.
In this post I will describe how to enable audit tracking for a specific set of attributes of an object and how to display the updates in a separate tab of the corresponding application.
I will use the Assets application for this tutorial and here is the final outcome I have achieved.

There are three configurations to be implemented for this:
  1. Enable audit of the main object and select attributes to be audited
  2. Define relationship to the audit table
  3. Customize the application to display the audit log from the UI

Enable audit

First step is to enable the auditing of the table you want to monitor.
Open the Database Configuration application, select ASSET object and check the Audit Enabled flag.

You now have to select which fields will trigger the audit record. Switch to the Attributes tab and select the Enable Auditing flag for STATUS, SERIALNUM, LOCATION, DESCRIPTION fields.

Enable admin mode, apply configuration changes and disable admin mode.
A new database table A_ASSET will be created and, from now on, all the changes to the audited fields will be traced in the audit table.

Define relationship

In order to be able to display the audit records we have to create a relationship from the ASSET table to the A_ASSET table.
Open the Database Configuration application, select ASSET object and in the Relationships tab create the following relationship.
  • Relationship: A_ASSET
  • Child Object: A_ASSET
  • Where Clause: assetnum=:assetnum and siteid=:siteid

Customize application

Now you can use the relationship you have just created to display records from the child A_ASSET audit table in the Asset application.
Open Application Designer application, select the ASSET application and export the app's XML definition.
Backup the file and edit it with a text editor. Paste the text below before the last </tabgroup> before the </clientarea> tag.

<tab id="myhist" label="History">
  <section id="myhist_grid1">
    <sectionrow id="myhist_grid1_11">
      <sectioncol id="myhist_grid1_11_1">
        <section id="myhist_grid1_11_1_1">
          <multiparttextbox dataattribute="assetnum" descdataattribute="description" id="myhist_grid1_1"/>
  <table id="myhist_t1" inputmode="readonly" label="Asset history" orderby="EAUDITTIMESTAMP desc" relationship="A_ASSET">
    <tablebody displayrowsperpage="10" id="myhist_t1_tb">
      <tablecol dataattribute="SERIALNUM" id="myhist_t1_SERIALNUM"/>
      <tablecol dataattribute="STATUS" id="myhist_t1_STATUS"/>
      <tablecol dataattribute="LOCATION" id="myhist_t1_LOCATION"/>
      <tablecol dataattribute="DESCRIPTION" id="myhist_t1_DESCRIPTION"/>
      <tablecol dataattribute="EAUDITUSERNAME" id="myhist_t1_EAUDITUSERNAME" label="Changed By"/>
      <tablecol dataattribute="EAUDITTIMESTAMP" id="myhist_t1_EAUDITTIMESTAMP" label="Changed Date"/>

June 3, 2016

Watson IoT MQTT APIs walkthrough

After several experiments with IBM Watson IoT Platform I have decided to take a step back and play MQTT manually to really understand how it works. In this post I will walk through my experiments with of the Watson IoT Platform MQTT messaging API for devices, applications and gateways.


First of all you need an account on IBM Watson IoT Platform on Bluemix. In all cases you need to create your own Internet of Things Platform service and create your own device type and device. I suggest you to follow the IoT Starter tutorial if you are on familiar with Watson IoT.

Another important preliminary step is to have an MQTT client to simulate MQTT calls. I have decided to use MQTT Spy tool. Download it from here and have it ready on your PC. There is a very good tutorial here if you want to learn more.
If you don't like it you can also try MQTT Lens.

Publish event from a device

This is the most simple example. A device sending MQTT messages to the cloud.
The device must be first registered on Watson IoT Platform. On you IBM Bluemix dashboard, click on the Internet of Things Platform service. Open the dashboard by clicking on the 'Launch Dashboard' button.
Open the Devices page and click on the Create Device button. First create a Device Type and then a new Device with the following details:
  • Type: TestDeviceType
  • Name: TestDev1
  • Security token: test1234
A summary page will be displayed with the device information needed for the connection.

Now we are ready to send our first message.
Devices use the following identification/authentication information (detailed documentation):
  • Client ID in formatted as d:[OrgId]:[DeviceType]:[DeviceId]
  • Username is use-token-auth
  • Password is the authentication token generated during device registration
  • Topic for publishing events is iot-2/evt/status/fmt/json

Create an new connection on MQTT Spy (replace [OrgId] with your Organization ID).
  • Protocol version: MQTT 3.1.1
  • Server URI: [OrgId]
  • Client ID: d:[OrgId]:TestDeviceType:TestDev1
  • Username: use-token-auth
  • Password: test1234

Now you can publish the first MQTT message.
  • Topic: iot-2/evt/status/fmt/json
  • Data: {"d":{"status":"Hello Watson IoT"}}

Now open the Watson IoT device details and click on the Publish button on MQTT Spy. You should see the incoming message like this.

Publish event from an application on behalf of the device

The Watson Iot Platform allow to define external applications that can publish events on behalf of any device and subscribe to device events. In this tutorial we will publish a message for the Dev1 device and subscribe to its events.

We first need to define the application on Watson Iot Platform Dashboard. Open the Access and API Keys tab, then click on Generate API Keys button. Take note of the API Key and Authentication Token.

Applications use the following identification/authentication information (detailed documentation):
  • Client ID in formatted as d:[OrgId]:[AppId]
  • Username id the API Key generated during registration
  • Password is the Authentication Token generated during registration
An application can publish events as if they came from any registered device.
  • Topic for publishing events is iot-2/type/[DeviceType]/id/[DeviceId]/evt/[EventId]/fmt/json

Create an new connection with your MQTT client.
  • Protocol version: MQTT 3.1.1
  • Server URI: [OrgId]
  • Client ID: a:[OrgId]:App1
  • Username: enter your API Key
  • Password: enter your Authentication Token

Now you can publish a message on behalf of device TestDev1 used before.
  • Topic: iot-2/type/TestDeviceType/id/TestDev1/evt/status/fmt/json
  • Data: {"d":{"status":"Hello Watson IoT", "sender":"I'm App1"}}

Subscribe to device events from an application

An application can subscribe to events from one or more devices using topic iot-2/type/[DeviceType]/id/[DeviceId]/evt/[EventId]/fmt/json
The MQTT wildcard character '+' can be used to subscribe to more than one type of event.

On MQTT Spy subscribe to all events from the TestDev1 device using topic iot-2/type/TestDeviceType/id/TestDev1/evt/+/fmt/json

Play with MQTT Spy and see how App 1 is notified when publishing events from the device. Try changing the subscription topic as well.


Gateways can publish events from itself and on behalf of any device connected to it. In this tutorial we will publish a message for the Dev1 device and see how it is received from the subscribed app.

Open the Devices page on your Watson IoT dashboard and click on the Add Device button. First create a Gateway Type and then a new Gateway.
  • Type: TestGwType
  • Name: TestGw1
  • Security token: test1234

Gateways use the following identification/authentication information (detailed documentation):
  • Client ID in formatted as g:[OrgId]:[GwType]:[GwId]
  • Username is use-token-auth
  • Password is the authentication token generated during device registration
Note that these information are very similar to the device connection properties but the Client ID has now a 'g' prefix instead of 'd'.
A gateway can publish events on behalf of a device.
  • Topic for publishing events is iot-2/type/[DeviceType]/id/[DeviceId]/evt/[EventId]/fmt/json

Create an new connection with your MQTT client.
  • Protocol version: MQTT 3.1.1
  • Server URI: [OrgId]
  • Client ID: g:[OrgId]:TestGwType:TestGw1
  • Username: use-token-auth
  • Password: test1234

Publish an event for the gateway:
  • Topic: iot-2/type/TestGwType/id/TestGw1/evt/status/fmt/json
  • Data: {"d":{"status":"Hello Watson IoT", "sender":"I'm Gw1"}}
Publish an event for the device:
  • Topic: iot-2/type/TestDeviceType/id/TestDev1/evt/status/fmt/json
  • Data: {"d":{"status":"Hello Watson IoT", "sender":"I'm Gw1"}}
Note haw the App1 subscription is receiving the events from the device. You can also try to subscribe to the gateway's events.


IBM Watson IoT Platform documentation
MQTT Spy tutorial

May 7, 2016

Install Kura on Raspberry PI

Eclipse Kura is an Open Source project that provides a platform for building IoT gateways. It is a smart application container that enables remote management of such gateways and provides a wide range of APIs for allowing you to write and deploy your own IoT application. Kura runs on top of the Java Virtual Machine (JVM) and leverages OSGi, a dynamic component system for Java, to simplify the process of writing reusable software building blocks. Kura APIs offer easy access to the underlying hardware including serial ports, GPS, watchdog, USB, GPIOs, I2C, etc. It also offer OSGI bundle to simplify the management of network configurations, the communication with IoT servers, and the remote management of the gateway.

In this post I will describe my own quick way of installing and configuring Kura on a Raspberry PI board. Please refer to the official Kura installation guide for more information.


  • Raspberry PI 2 or 3 with a recent version of Raspbian OS and connected to the internet. See my quick start.

Download Kura package

First of all you need to download Kura on your PI from this page.
Identify the package to be downloaded from the list. I typically use the Extended, No Net, with Web UI version - Kura 1.4.0 Extended Download: Raspbian (Model 2, No Net, with Web UI).
Versions with network are a little bit more challenging to install.

Download the selected package using the web browser or you can use this command:
cd /tmp

Install Kura

First update your system's package list by entering the following command:
sudo apt-get update

Install the Kura package:
sudo dpkg -i kura_1.4.0_raspberry-pi-2-nn_installer.deb

Ignore the dependency problems warnings and fix the installation by running the following command:
sudo apt-get install -f

You can now delete the Kura package:
rm kura_1.4.0_raspberry-pi-2-nn_installer.deb

Reboot the Raspberry Pi
sudo reboot

Kura server will be restarted automatically so you should be able to access the Kura administrative UI - http://[HOST]/kura
Default login is admin/admin

To view Kura logs type the following command
tail -f /var/log/kura.log

It is strongly suggested to change admin password. This can be done by selecting the WebConsole service from the UI.

Disable CloudService (optional)

In my examples I will connect Kura to IBM Watson IoT Platform. The CloudService is a proprietary service and not appropriate for connecting to Watson IoT so we can disable it permanently.

Edit the Kura /opt/eclipse/kura/kura/config.ini file and modify the osgi.bundles parameter by changing the action for the*.jar from "start" to "stop".

May 6, 2016

Raspberry PI installation and first setup

In this post I will describe how to install the Raspbian operating system on a Raspberry PI 2 or 3 and some basic configuration settings to start your IoT projects.
The described procedure will not require a display and keyyboard and can be performed just connecting the PI to your router using a LAN cable.

Raspbian OS installation

First of all you need to download the latest Raspbian OS image from here. This procedure is tested with Raspbian Jessie with Pixel (version January 2017).
If you are running on Windows, download the Win32DiskImager utility from here and use it to flash the ISO image to the SD card. Refer to this guide for more details. If you are running on Linux or MaxOS look here.

Do not remove the SD card from the PC at the end of the process because we first need to enable SSH because, due to security reasons, SSH protocol is disabled by default in the latest versions of Raspbian OS.
To enable SSH, all you need to do is to put a file called ssh in the boot partition. The contents of the file don’t matter: it can contain any text you like, or even nothing at all.

You can now insert the SD card into the Raspberry PI slot and power it on.

Basic configuration

To avoid the need of connecting a display and keyboard, I typically just plug a LAN cable from my home router to the PI and get it's IP address from the router administrative console which typically is accessible at or

You can now use any SSH client like Putty to connect to the PI. The default user and passwords are pi/raspberry.

Set keyboard (optional)

Run raspi-config again if needed.
sudo raspi-config

Choose ‘4 Localisation Options’.
Set the correct timezone and keyboard layout.
I typically leave the default locale en_GB.UTF-8 UTF-8.

Running VNC server

The new Raspbian with Pixel already comes with VNC server installed so all you have to do is start it.
On the shell just type:

The last line of the output should be something like this:
New desktop is raspberrypi:1 (

Now the VNC server on port 1 should be started and you can use any VNC client to connect to the PI. I personally use the RealVNC client.

Connect to your device with the VNC client and enter credentials (pi/raspberry). A security warning should now appear to warn you it's time to change your password.
Run the Raspberry PI Configuration application under Preferences menu. From here you can change the default password.

In the Interfaces tab you can also automatically start the VNC server at boot.

The VNC resolutin can be forced in the /boot/config.txt file. Inside the VNC remote session, open a teminal and type:
sudo leafpad /boot/config.txt

Enter the follwing settings to force a 1080p resolution (1920x1080) . See here for more details.

Reboot your Raspberry Pi

Wait for 1 or 2 minuter for the PI to restart and try connecting to the VNC server to check if it automatically starts.

If you have a RasPI 3 you can now easily configure the WiFi connection from the desktop. After having configured the WiFi, disconnect the LAN cable. Don't forget that a new IP address will be assigned.

Port Mapping (optional)

If you have installed the Raspberry PI at your house and want to access it remotely you can define port mappings on your router.
  • SSH: 22 > 10022
  • VNC: 5901 > 15901
  • HTTP: 80 > 10080
I prefer to change the ports for security reasons and to allow more than one device to be mapped.

May 4, 2016

IoT and IIoT

The Internet of Things (IoT) is one of the most significant trends in technology today. A melding of innovations in the fields of computing and communication, IoT and its “smart” devices are poised to revolutionize not only user-machine interaction but also the way in which machines engage with one another.

IoT is now permeating in every industry energy, healthcare, automotive, and other industries are beginning to grapple with the Industrial of Internet of Things (IIoT), where devices such as sensors, robots, mixing tanks, and insulin pumps are becoming increasingly more connected.

But what’s the difference between IoT and IIoT? It’s not a difficult distinction, but many people incorrectly use them interchangeably.

IoT vs. IIoT: Consumer vs. Industrial Applications
If your refrigerator or wearable device is connected to the Internet, then it is part of the IoT. It’s a consumer application. If your factory floor has machinery connected to each other and to a centralized data center, it is an M2M application. If your factory floor has an ecosystem of sensors, machinery and people connected to processes and to the Internet, then it is part of IIoT. It’s an industrial application.

Many analysts say that IIoT will drive the next industrial revolution (Industry 4.0) where intelligent, interconnected systems will seamlessly support activities along the entire value chain and boost efficiency and productivity.

A major concern surrounding the Industrial IoT is interoperability between devices and machines that use different protocols and have different architectures. The nonprofit Industrial Internet Consortium, founded in 2014, focuses on creating standards that promote open interoperability and the development of common architectures.

Industrial IoT has far more stringent requirements than the consumer IoT, including the need for no-compromise control, rock-solid security, unfailing reliability even in harsh (extremely hot or cold, dusty, humid, noisy, inconvenient) environments, and the ability to operate with little or no human intervention. And unlike more recently designed consumer-level devices, many of the billion or so industrial devices already operating on existing networks were put in place to withstand the test of time, often measured in decades.

The differences between the IIoT and IoT are not just a matter of slight degree or semantics. If your Fitbit or Nest device fails, it might be inconvenient. But if a train braking system fails, it could be a matter of life and death.

TechCrunch - Making Sense Of The Internet Of Things
Tripwire - 5 Key Challenges for the Industrial Internet of Things (IIoT)
IoT Agenda - Industrial Internet of Things (IIoT)
The Industrial IoT isn’t the same as the consumer IoT

May 3, 2016

MxLoader now supports Web Services

By default MxLoader uses plain Object Services through HTTP calls. These services are automatically exposed when an Object Structure is created in Maximo. Using Web Services is possible to have more control over data defining processing classes, user exit classes, XSL maps, and processing rules.

MxLoader 5.1 now allows to use Web Services as well. To enable MxLoader to work with Web Services you need two settings in the Config sheet:
  • Server address must be changed to http://[MXHOST]/meaweb/services/
  • Use Web Service option must be set to ‘True’
When a WebService is defined from an Object Structure you can just specify the WS name in cell A2 as you normally do. However, when the WebService is defined from an Enterprise Service you have to specify the web service name with a dotted notation in cell A2 with format [EXTSYSNAME]_[ESNAME].[OSNAME]. For example, if your External System is ‘MYEXTSYS’ your Enterprise Service is named ‘MYESVC’ and based on ‘MXASSET’ you have to specify MYEXTSYS_MYESVC.MXASSET in cell A2.

You can download MxLoader from here.
 Please use MxLoader forum to report any issue.

April 19, 2016

Display bullettin board messages opened by default

A common requirement is to display the bulletin board messages in a more visible way.
A simple solution is to display the formatted message opened by default.

To achieve this you can edit the following JSP file: [SMP]\applications\maximo\maximouiweb\webmodule\webclient\components\bulletinboard.jsp

On my Maximo i have this line of code at row 335:


If you change the display attribute to 'inline' it will show the full message without clicking.


Rebuild and redeploy maximo.ear file and you are done.

April 6, 2016

Maximo Anywhere API Reference

The configuration and customization chapters of the Maximo Anywhere documentation is great for many scenarios but sometimes you need to work with Dojo scripting and Anywhere platform to implement changes to standards screens and business rules.

I have developed my own Anywhere API Reference with the objective of helping myself and the Anywhere technical community out there.

Feedback is welcome but please do not post comments with generic questions I don't have time to answer to all of you.


Package platform.model

Package ui.control

April 5, 2016

Restricting status changes in Maximo Anywhere

A common requirement in Maximo projects is to restrict work order status changes based on some internal business rules. This can be achieved on Maximo with the technique described in this article.
Unfortunately the rules set in the WOSTATUS domain on the server are not applied automatically to the Anywhere mobile applications so we need to customize the mobile apps to follow the same rules.

Lets first analyze how the status changes are managed in the Work Execution app.
The script implements a state machine where the matrix of allowed status changes are defined (state machine).

init: function(modelDataSet){
var settings = {};
settings.resourceName = "workOrder";
settings.stateToAlias = {};
settings.configuration = {
"WAPPR": ["WAPPR", "APPR", "INPRG",         "WMATL", "COMP",         "CAN"],
"APPR" : ["WAPPR", "APPR", "INPRG",         "WMATL", "COMP",         "CAN"],
"INPRG": ["WAPPR",         "INPRG",         "WMATL", "COMP"               ],
"WSCH":  ["WAPPR", "APPR", "INPRG", "WSCH", "WMATL", "COMP"               ],
"WMATL": ["WAPPR",         "INPRG",         "WMATL", "COMP",         "CAN"],
"COMP":  [                                           "COMP"               ],
"CLOSE": [                                                   "CLOSE"      ],
"CAN":   [                                                           "CAN"]

Unfortunately this script is referenced in many other scripts so the simplest may of overriding it is modifying it. This is not a best practice since this file may be overridden in an Anywhere fixpack but is a quick and dirty solution.
Another limitation of this solution is that it works only based on the domain internal values (SYNONYMDOMAIN.MAXVALUE).

Now imagine the following scenario. I have two aliases of the INPRG status: INPRG1 and INPRG2. I want to avoid a direct transition to INPRG2. Only INPRG1 to INPRG2 must be possible.

Another place where status changes are handled is the filtering rule in the WorkExecution.statusLookup lookup.

<lookup id="WorkExecution.statusLookup" label="Work Order Status" resource="domainwostatus"

If you look at the application.handlers.StatusChangeHandler.filterWOStatus(evenHandler) method you will notice it handles some logic and filters the visible status in the lookup. We can now override this method to implement our logic.

First of all create a new script custom.handlers.MyStatusChangeHandler.

[ "dojo/_base/declare",
  "application/business/WorkOrderStatusHandler" ],
function(declare, ApplicationHandlerBase, StatusChangeHandler, WorkOrderStatusHandler) {
return declare( [ApplicationHandlerBase, StatusChangeHandler], {

  // dummy method required only to ensure script loading
  dummyMethod: function(eventContext) {
  // Filter WO statuses to those available for selection
  filterWOStatus: function(eventContext) {
    // call the original filterWOStatus method

    // retrieve current status
    var currWO = eventContext.application.getResource("workOrder").getCurrentRecord();
    var currStatus = currWO.get("status");

    // retrieve wodomain resource
    var woStatusDomain = this.application.getResource('domainwostatus');

    // sets the additional filter if necessary
    if(woStatusDomain.isFiltered() && currStatus!="INPRG1"){
      woStatusDomain.filter("value!=$1", "INPRG2");


There are two important things to note here.
  1. How the original StatusChangeHandler script is extended using the (see Dojo declare tutorial for details)
  2. How the additional filter value!='INPRG2" is applied to the existing filter already set in the woStatusDomain resource.
  3. The filter is specified using JavaScript syntax not SQL (JavaScript conditions syntax).
Now you need to modify the app.xml to include your customized transition rule.
Modify the WorkExecution.statusLookup filterClass from application.handlers.StatusChangeHandler to custom.handlers.MyStatusChangeHandler.

It seems also this is not enough because the custom script is not loaded by just referencing it in the filterClass.To circumvent this problem you can add a dummy eventhandler like this.

<eventHandler event="render" 
class="custom.handlers.MyStatusChangeHandler" method="dummyMethod" />

March 25, 2016

Remove the 200 record limit in Anywhere simulator

The mobile app simulator is a great tool when developing mobile applications with Maximo Anywhere. Obviously it does not replace a good test on the real device but it is essential during development especially when the browser development tools are used to inspect and debug the app.
One limitation of the simulator is the 200 records limit when downloading lookup data so the following message is displayed: When running in a browser, a maximum of 200 records are downloaded per lookup.

Set debug="true" attribute to the app element at the beginning of the app.xml file.

However, if you have a lot of data to download this may cause a hang during the lookup data download in the web browser. This is because the mobile browser storage is limited to few megabytes.

To circumvent this problem you can try to modify
WorkExecution\common\js\platform\store\_ServerDataDownloadManager.js file.
Search for '200' and you should see something like this.

if (limitData && pageSize > 200) {
  pageSize = 200;

Change both numbers to 500 or whatever number fits your needs.

March 24, 2016

Maximo Anywhere Simulator issues with Google Chrome

You know that Google Chrome is the preferred browser for previewing mobile apps when developing with Maximo Anywhere. However, it seems that Google Chrome automatic updates are a big pain because sometime the simulator stops working correctly.

I know there were at least 3 problems caused by a Chrome update:
  • Menu is not displayed correctly
  • Mobile app is not responding to clicks
  • Javascript error: win.doc.getCSSCanvasContext is not a function
I these situations you can always open a PMR to get a fix from IBM but I believe there is a better strategy. Use Chromium !
It is the OpenSource version of Chrome sharing the same code base but with no automatic update.
You can download it from here:
I'm currently using Cromium 44.0.2383.0 and I no longer have to worry for a bad update.