Creating Additional KPIs
Additional KPIs can be added, using the availability, OEE, quality ratio, and effectiveness KPI implementations as a model.
The high-level steps and best practices for creating an additional KPI are as follows:
1. Create a thing shape for your KPI, using the following naming convention: (<Prefix>.KPI) + <KPI_NAME> + ThingShape. For example, MY_CORP.KPI.MyKPIThingShape.
On the thing shape, add the following properties, as a minimum:
<KPI_Name>_currentValue, with Base Type=Number
<KPI_Name>_lastCalculatedTime, with Base Type=DateTime
<KPI_Name>_unitOfMeasure, with Base Type=String
Ensure that the Persistent checkbox is selected for each of these properties.
2. Implement the following services on your KPI thing shape, using the services on PTC.SCA.SCO.OEEThingShape as an example:
Get_<KPI_Name>_CurrentValue
Get_<KPI_Name>_ThresholdValues
Get_<KPI_Name>_Trend
<KPI_Name>_Calculate
Set_<KPI_Name>_CurrentValue
3. On the thing template of each equipment type for which this KPI is to be calculated, override the GetKPINames service to add the new KPI thing shape. See the GetKPINames service on the PTC.SCA.SCO.KPIElementThingShape for an example.
4. Declare the thresholds for the KPI in the KPI manager. The out-of-the-box KPI manager is the PTC.SCA.SCO.DefaultKPIManager.
a. On the KPI manager thing, add a configuration table with the following values:
Table Name<KPI_Name>ThresholdValues
Data ShapePTC.SCA.SCO.KPIThresholdValues
Allow Multiple Rows—Ensure that this checkbox is selected.
b. Add at least one row to the new configuration table. Use the existing KPI threshold value configuration tables as an example.
5. If your <KPI_Name>_Calculate service depends on a new KPI element, define the KPI element on the thing shape created for your KPI in step 1:
a. Create a property for the KPI element with the appropriate Base Type for your KPI element, most commonly Number. Ensure that the Persistent checkbox is selected.
b. Create a new service named <property_name>_Calculate. Refer to the producedQuantity_PQ_Calculate service as an example.
c. On the thing template of each equipment type for which this KPI is to be calculated, override the GetKPIElementNames service. Add the new KPI Element property to the list of KPI elements.
Example 1: Rework Ratio KPI
This example creates a new KPI named Rework Ratio (ISO_22400). The rework ratio is the relationship between the rework quantity (RQ) and produced quantity (PQ) for a work unit, product, production order, or defect type:
RQ/PQ
The rework quantity (RQ) KPI element is created as part of this example.
1. Create a new thing shape named ACME_CORP.KPI.ReworkRatioThingShape with the following properties:
ReworkRatio_currentValue,with Base Type=Number
ReworkRatio_lastCalculatedTime, with Base Type=DateTime
ReworkRatio_unitOfMeasure, with Base Type=String
Ensure that the Persistent checkbox is selected for each of these properties.
2. Implement the following services on the ACME_CORP.KPI.ReworkRatioThingShape thing shape:
Get_ReworkRatio_CurrentValue
Get_ReworkRatio_ThresholdValues
Get_ReworkRatio_Trend
ReworkRatio_Calculate
Set_ReworkRatio_CurrentValue
3. On the thing template of each equipment type for which this KPI is to be calculated, override the GetKPINames service. Add the ACME_CORP.KPI.ReworkRatioThingShape thing shape, as in the following example code:
var kpiInfoJSON = new Object();
// JSON of KPI information we want to test if they are implemented on "me"
// For custom KPI, please add into this JSON and following given format
// Warning the order is important
kpiInfoJSON.kpiInfoArray = [
{kpiThingShapeName: 'PTC.SCA.SCO.AvailabilityThingShape', kpiName: 'Availability'},
{kpiThingShapeName: 'PTC.SCA.SCO.QualityRatioThingShape', kpiName: 'QualityRatio'},
{kpiThingShapeName: 'PTC.SCA.SCO.EffectivenessThingShape', kpiName: 'Effectiveness'},
{kpiThingShapeName: 'PTC.SCA.SCO.OEEThingShape', kpiName: 'OEE'},
{kpiThingShapeName: 'ACME_CORP.KPI.ReworkRatioThingShape', kpiName: 'ReworkRatio'}
];
var result = Things["PTC.SCA.SCO.DefaultKPIManager"].GetImplementedKPIsOnThing({
thingName: me.name,
kpiInfo: kpiInfoJSON
});
4. Declare the thresholds for the Rework Ratio KPI. On the Configuration page of the PTC.SCA.SCO.DefaultKPIManager thing, add a new configuration table.
a. On the PTC.SCA.SCO.DefaultKPIManager thing, add a configuration table with the following values:
Table NameReworkRatioThresholdValues
Data ShapePTC.SCA.SCO.KPIThresholdValues
Allow Multiple Rows—Ensure that this checkbox is selected.
b. Add rows to the ReworkRatioThresholdValues configuration table for the desired value ranges, similar to the following image:
Screenshot of the ReworkRatioThresholdValues configuration table created as part of this example
5. Define the rework quantity (RQ) KPI element.
a. Create a property on the ACME_CORP.KPI.ReworkRatioThingShape thing shape named reworkQuantity_RQ, with a Base Type of Number, and the Persistent checkbox selected.
b. Create a new service on the ACME_CORP.KPI.ReworkRatioThingShape thing shape named reworkQuantity_RQ_Calculate with the following code:
function isUndefinedOrNull(value){
return (value === null || typeof(value) === "undefined" );
}
result = me.reworkQuantity_RQ;
//Register first value to cache
me.RegisterValueToKPICache({
key: "reworkQuantity_RQ_firstValue" /* STRING */,
value: result, /* NUMBER */
TimeInfo : TimeInfo
});
c. On the thing template of each equipment type for which this KPI is to be calculated, override the GetKPIElementNames service. Add the new property to the list. For example:
result = "actualProductionTime_APT,goodQuantity_GQ,plannedBusyTime_PBT,plannedRunTimePerItem_PRI,producedQuantity_PQ,reworkQuantity_RQ";
d. Update the ReworkRatio_Calculate service created in step 2 to include the new rework quantity (RQ) KPI element. For example:
var customLogger = logger.getLoggerContext().getLogger(logger.getName()+".com.ptc.sca.sco.KPICalculation.ReworkRatio");
var reworkRatio = me.reworkQuantity_RQ;
var producedQuantity = me.producedQuantity_PQ;
if (TimeInfo.getRowCount() > 0){
var row = TimeInfo.getRow(0);
var startTime = row.StartTimeDate;
var generatedID= row.GeneratedID;
// result from service GetKPIElementStartValue: INFOTABLE dataShape: "PTC.SCA.SCO.KPIElementValue"
var reworkQuantity_RQ_firstValue = me.GetKPICacheValue({
generatedID: generatedID/* STRING */,
name: "reworkQuantity_RQ_firstValue" /* STRING */
});
var producedQuantity_PQ_firstValue = me.GetKPICacheValue({
generatedID: generatedID/* STRING */,
name: "producedQuantity_PQ_firstValue" /* STRING */
});
var producedTotalQuantity = 0;
var producedReworkQuantity = 0;
if ( (typeof producedQuantity_PQ_firstValue !== 'undefined') && (typeof reworkQuantity_RQ_firstValue !== 'undefined') ){
producedTotalQuantity = (producedQuantity - producedQuantity_PQ_firstValue);
producedReworkQuantity = (reworkRatio - reworkQuantity_RQ_firstValue);
if ( producedTotalQuantity === 0 ){
// Until items are produced, rework ratio cannot be measured and will default to 1
// with producedReworkQuantity greater than 0, then the service will return 0.
reworkRatio = producedReworkQuantity > 0 ? 0: 1;
}
else{
reworkRatio = producedReworkQuantity / producedTotalQuantity;
}

customLogger.debug("ReworkRatio_Calculate: produced quantity value when time info started - last produced quantity :"+ producedQuantity_PQ_firstValue+"-"+producedQuantity);
customLogger.debug("ReworkRatio_Calculate: rework quantity value when time info started - last good quantity: "+ reworkQuantity_RQ_firstValue+"-"+reworkRatio);
customLogger.debug("ReworkRatio_Calculate: produced Rework Quantity / produced total quantity = quality ratio: " +producedReworkQuantity+ " / " + producedTotalQuantity + " = " + reworkRatio);
}
else{
customLogger.warn("ReworkRatio_Calculate for "+me.name+" could not retrieve the good quantity or produced quantity at the start of the time info.");
}
}
else{
customLogger.warn("ReworkRatio_Calculate for "+me.name+" could not retrieve the current time info. ReworkRatio = 0.");
}
var result = reworkRatio;
me.Set_ReworkRatio_CurrentValue({
value: result
});
var now = new Date();
me.ReworkRatio_lastCalculatedTime = now;

Example 2: Technical Efficiency KPI
This example creates a new KPI named Technical Efficiency (ISO_22400). The technical efficiency of a work unit is the relationship between the actual production time (APT) and the sum of the actual production time (APT) and actual unit delay time (ADET), including delays and malfunction-caused interruptions.
APT/(APT+ADET)
The actual unit delay time (ADET) KPI element is created as part of this example. The code samples in this example assume that the Rework Ratio KPI has already been implemented.
1. Create a new thing shape named ACME_CORP.KPI.TechnicalEfficiencyThingShape with the following properties:
TechnicalEfficiency_currentValue, with Base Type=Number
TechnicalEfficiency_lastCalculatedTime, with Base Type=DateTime
TechnicalEfficiency_unitOfMeasure, with Base Type= String
Ensure that the Persistent checkbox is selected for each of these properties.
2. Implement the following services on the ACME_CORP.KPI.TechnicalEfficiencyThingShape thing shape:
Get_TechnicalEfficiency_CurrentValue
Get_TechnicalEfficiency_ThresholdValues
Get_TechnicalEfficiency_Trend
TechnicalEfficiency_Calculate
Set_TechnicalEfficiency_CurrentValue
3. On the thing template of each equipment type for which this KPI is to be calculated, override the GetKPINames service to add the ACME_CORP.KPI.TechnicalEfficiencyThingShape thing shape. For example:
var kpiInfoJSON = new Object();
// JSON of KPI information we want to test if they are implemented on "me"
// For custom KPI, please add into this JSON and following given format
// Warning the order is important
kpiInfoJSON.kpiInfoArray = [
{kpiThingShapeName: 'PTC.SCA.SCO.AvailabilityThingShape', kpiName: 'Availability'},
{kpiThingShapeName: 'PTC.SCA.SCO.QualityRatioThingShape', kpiName: 'QualityRatio'},
{kpiThingShapeName: 'PTC.SCA.SCO.EffectivenessThingShape', kpiName: 'Effectiveness'},
{kpiThingShapeName: 'PTC.SCA.SCO.OEEThingShape', kpiName: 'OEE'},
{kpiThingShapeName: 'ACME_CORP.KPI.ReworkRatioThingShape', kpiName: 'ReworkRatio'},
{kpiThingShapeName: 'ACME_CORP.KPI.TechnicalEfficiencyThingShape', kpiName: 'TechnicalEfficiency'}

];
var result = Things["PTC.SCA.SCO.DefaultKPIManager"].GetImplementedKPIsOnThing({
thingName: me.name,
kpiInfo: kpiInfoJSON
});
4. Declare the thresholds for the Technical Efficiency KPI. On the Configuration page of the PTC.SCA.SCO.DefaultKPIManager thing, add a new configuration table.
a. On the PTC.SCA.SCO.DefaultKPIManager thing, add a configuration table with the following values:
Table NameTechnicalEfficiencyThresholdValues
Data ShapePTC.SCA.SCO.KPIThresholdValues
Allow Multiple Rows—Ensure that this checkbox is selected.
b. Add rows to the TechnicalEfficiencyThresholdValues configuration table for the desired value ranges.
5. Define the actual unit delay time (ADET) KPI element.
a. Create a property on the ACME_CORP.KPI.TechnicalEfficiencyThingShape thing shape named actualUnitDelayTime_ADET, with a Base Type of Number, and the Persistent checkbox selected.
b. Create a new service on the ACME_CORP.KPI.TechnicalEfficiencyThingShape thing shape named actualUnitDelayTime_ADET_Calculate with the following code:
var adet = me.CalculateTimeElement({
elementName: "ADET", /* STRING */
TimeInfo : TimeInfo
});

me.actualUnitDelayTime_ADET = adet;
var result = adet;
c. On the thing template of each equipment type for which this KPI is to be calculated, override the GetKPIElementNames service. Add the new property to the KPI element list. For example:
result = "actualProductionTime_APT,goodQuantity_GQ,plannedBusyTime_PBT,plannedRunTimePerItem_PRI,producedQuantity_PQ,reworkQuantity_RQ,actualUnitDelayTime_ADET";
d. Update the TechnicalEfficiency_Calculate service created in step 2 to include the new actual unit delay time (ADET) KPI element. For example:
customLogger = logger.getLoggerContext().getLogger(logger.getName()+".com.ptc.sca.sco.KPICalculation.TechnicalEfficiency");
var technicalEfficiency = 0;
var aptTime = me.actualProductionTime_APT;
var adetTime = me.actualUnitDelayTime_ADET;
customLogger.debug("TechnicalEfficiency_Calculate aptTime:"+aptTime+" adetTime:"+adetTime);
if ( aptTime !== 0){
technicalEfficiency = aptTime / (aptTime+adetTime);
}else{
technicalEfficiency = 1;
}
customLogger.debug("TechnicalEfficiency_Calculate TechnicalEfficiency:"+technicalEfficiency);
me.Set_TechnicalEfficiency_CurrentValue({
value: technicalEfficiency
});
me.TechnicalEfficiency_lastCalculatedTime = Date.now();
var result = technicalEfficiency;
6. As the actual unit delay time (ADET) KPI element is a time element, update the appropriate status definitions to include ADET as a timeElement value. Statuses with timeElement values are taken into account when the specified KPI elements are calculated. For this example, configure the Custom1 status to have a timeElement value of ADET,PBT, so that the Custom1 status is taken into account when the ADET and PBT are calculated.
Screenshot thowing the Custom1 status configured as described in this step of the example.
For more information, see Configuring Equipment Statuses.
Was this helpful?