Community Tip - Visit the PTCooler (the community lounge) to get to know your fellow community members and check out some of Dale's Friday Humor posts! X
This is a useful trick for rolling up metrics in Thingworx across various levels of a hierarchy by using Networks, ThingShapes, and recursive service definitions.
Say that you have a hierachy of Things in your model such as a Global view, a Region view, and a Store view -- this could be a Mfg Plant, a Building, an Asset, etc; whatever the core metric producing Thing is in your model -- where your Store has KPIs that you want to roll up across regions and globally.
First, create a template for each of your hierarchical levels. In my case it is a GlobalTemplate, RegionalTemplate, and StoreTemplate. Add a property to your StoreTemplate that will be the KPI.
Now, create a Thing for the Globe, and each of your Regions and Stores. Add them to a hierachical Network as such:
Now, we need to create a ThingShape to aggregate our KPIs and apply it to the Global, Regional, and Store template.
Now we will define a recursive funciton on our ThingShape called GetKPI and define it with the following:
//define our base case, when the thing template we are on is the lowest level of our hierarchy, in this case the StoreTemplate
if (me.thingTemplate == "StoreTemplate") {
//in our base case, the result is just the property for the metric we want to aggregate
result = me.someMetric
} else {
//otherwise, we are at some other level in the hierarchy and we need to get our child connections from the network
//this gets all the things below us in the network
var params = {
name: me.name /* STRING */
};
// result: INFOTABLE dataShape: NetworkConnection
var network = Networks["Network"].GetChildConnections(params);
//loop through each of the things below us in the hierarchy and recursively add the result of GetKPI() to our result
result = 0;
for each (var row in network.rows) {
result += Things[row.to].GetKPI();
}
}
This is a simple case of just summing up a single property, but we can take this further using the Union and Aggregate snippets provided by thingworx to do other kinds of summarization. First add a new property called someAvgMetric to our StoreTemplate, and define a new service GetKPIProperties as such, with an InfoTable result, on the StoreTemplate
varparams = {
propertyNames: {"items": ["someMetric", "someAvgMetric"]} /* JSON */
};
// result: INFOTABLE dataShape: "undefined"
var result = me.GetNamedProperties(params);
Now, define a new service on our ThingShape to utilize this service as our base case, and aggregate the resulting InfoTable when necessary. We'll call this service GetKPIAggregates:
//define our base case
if (me.thingTemplate == "StoreTemplate") {
//this function will be on the StoreTemplate, and returns the base infotable
result = me.GetKPIProperties()
} else {
//grab our network
var params = {
name: me.name /* STRING */
};
// result: INFOTABLE dataShape: NetworkConnection
var network = Networks["Network"].GetChildConnections(params);
//need to create an empty infotable to union into. I glossed over this, but you'll need a datashape here
//create empty infotable
var result = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({ infoTableName: "InfoTable", dataShapeName: "KPIDataShape" });
//loop through and union each of our results to our new infotable
for each (var row in network.rows) {
var params = {
t1: result /* INFOTABLE */,
t2: Things[row.to].GetKPIAggregates() /* INFOTABLE */
};
var result = Resources["InfoTableFunctions"].Union(params);
}
//aggregate each of our fields
var params = {
t: result /* INFOTABLE */,
columns: "someMetric,someAvgMetric" /* STRING */,
aggregates: "SUM,AVERAGE" /* STRING */,
groupByColumns: undefined /* STRING */
};
// result: INFOTABLE
var result = Resources["InfoTableFunctions"].Aggregate(params);
//need to loop through each of our field names and make them match our base infotable
// infotable datashape iteration
var dataShapeFields = result.dataShape.fields;
for (var fieldName in dataShapeFields) {
var stringName = dataShapeFields[fieldName].name;
var params = {
t: result /* INFOTABLE */,
from: stringName /* STRING */,
to: stringName.split("_")[1] /* STRING */
};
// result: INFOTABLE
var result = Resources["InfoTableFunctions"].RenameField(params);
}
}
Now, in our mashups, we can use a DynamicThingShape and call our GetKPIs service at any level in our network, and our data will be aggregated correctly for whatever level we are at in the hierarchy!