Skip to main content

Connect To Your Tags

In this section, we'll connect equipment state and count tags to the assets.

Reference Materials: Configuring Tags - Learn how to connect your equipment to an asset.
Watch Connecting To Data Tutorial

Kanoa Ops supports a variety of data connectivity options, making an extensible system that can grow to collect data from additional systems as adoption increases and requirements evolve. Machine Performance and OEE data can be collected via PLCs and automation equipment through Ignition's built-in OPC server or Industry 4.0 protocols like MQTT. Web APIs allow Kanoa Ops to integrate with business systems like ERP to automatically download material specifications and production schedules. When automatic data collection is not an option, Kanoa Ops provides manual entry solutions so that every single asset can be accounted for.

Whenever an asset is OEE Enabled, Kanoa creates an instance of the assetOEE tag UDT, named Line, in the asset's folder in the Kanoa tag provider. For example, CoreForge\Philadelphia\Machining\Machine 1 gets [Kanoa]CoreForge/Philadelphia/Machining/Machine 1/Line.

The assetOEE UDT instance is the standard interface between Ignition tag data and Kanoa Ops. It is used to:

  • Receive real-time input values
  • Trigger count events
  • Trigger state events
  • Trigger mode or production changes
  • Provide live OEE data back to dashboards and screens

Ignition Designer tag browser showing an assetOEE UDT instance with the folders Configuration, Inputs, OEE, and oeeThresholds, followed by the tags Enable Quality, eqPath, EquipID, and refresh

Folder / tag areaPurpose
Root-level tagsAsset identity and path information
ConfigurationCounter IDs, the user events are recorded against, and initialization
InputsIncoming data used to create Kanoa events
OEELive outputs for OEE, counts, state, and production context
OEE ThresholdsThreshold values used by dashboards and visual indicators

For the other Kanoa tags and UDTs, see Kanoa Tags.

Root-level Tags​

EquipID, the asset ID from Kanoa, and eqPath, the asset's path in the hierarchy, together connect the Ignition tag instance back to the Kanoa asset record.

TagPurpose
EquipIDThe asset's Kanoa assetId
eqPathThe asset's Kanoa path, worked out from the tag's folder (CoreForge\Philadelphia\Machining\Machine 1)
refreshSet to true to look up EquipID again from eqPath, for example after renaming or moving the asset
Enable QualityWhen true, product, shift and mode changes on this asset trigger Kanoa Quality checks

MQTT Path Parameter​

The assetOEE UDT has one parameter, MQTT path (MQTTPath). Every tag in the Inputs folder is a reference tag bound to {MQTTPath}/Inputs/<tag name>, so setting this one parameter links the whole Inputs folder to another source tag structure: an assetInputs instance, an MQTT folder or a PLC UDT with matching Inputs tags. For example:

[Kanoa]CoreForge Sim/Site/Machining/Machine 1/assetInputs

Leave it blank to set each input tag's source individually instead.

Configuration Folder​

The Configuration folder holds values that support event creation and UDT behavior. You generally shouldn't modify it during normal configuration, but it's useful for debugging, verifying counter IDs and refreshing values when troubleshooting.

TagPurpose
infeedCounterId, outfeedCounterId, wasteCounterIdThe asset's infeed, outfeed and waste counters. They are created, or looked up if they already exist, when the tag starts
userIdThe user count and state events are recorded against (the tagChange user)
initializeTags, refreshRun on startup to load userId. Set refresh to true to load it again

Inputs Folder​

The Inputs folder is where the asset is connected to its source of data. At a minimum, infeed and outfeed counts and asset state should be connected. If not all counts are available, a single count, infeed or outfeed, can be connected and the missing counts will be calculated automatically. To determine OEE Quality, at least two counts are required. The Inputs tags are reference tags and support MQTT backflushing in the event of a network drop.

If operations are started through the MES Application (Operator screen) or by the schedule, state and counts are all you need to connect. If the asset is operated from a different HMI or SCADA, also connect the Mode, Work Order and Product Code tags to the source, to fully capture production information within the MES.

TagConnect it toWhat Kanoa does when it changes
Infeed CountYour infeed count source. If an infeed count isn't available, it's derived from outfeed + wasteRecords the change as a counter event on the asset's infeed counter. Counts can be running totals: if the value drops (a PLC reset), the new value is counted as the change
Outfeed CountYour outfeed count source. If an outfeed count isn't available, it's derived from infeed - wasteRecords a counter event on the outfeed counter, the same way
Waste CountYour waste count source. If a waste count isn't available, it's derived from infeed - outfeedRecords a counter event on the waste counter, the same way
StateThe asset's state codeRecords a state event. If the new and previous states are both downtime and nobody has coded the previous one yet, it updates that event instead of adding another, so an operator can code a stop before restarting. Bad quality records the asset's Off Line state
Machine IDThe cell that caused the state, for a line with cellsState is looked up against this cell's states. For example, on 'Packaging Line 1' with cells 'Filler', 'Capper' and 'Palletizer', set Machine ID to the Filler's assetId (7) and State to the code linking the Filler to 'CIP' (127). Leave blank to use the line's own states
Part RequiredA signal that a part is neededFlags the downtime event as needing a part. Used by the Reliability KPIs screen, which counts these as 'Breakdown' losses
ModeThe asset's mode, if it can be read from a tagStarts a mode change for the asset, with the current work order and product
Work OrderThe production order running, if it can be read from a tagStarts a mode change for the asset with the new work order
Product CodeThe product running, if it can be read from a tagStarts a production run of that item, or goes Idle when blank. The order and item are created if they don't exist
ToolingThe serial or part number of the jig or mold in useTracks counts against a tool, so you can be notified when it needs replacing or maintenance, trigger quality checks on a tool change, and record which tool produced which lot

OEE Folder​

The OEE folder holds live values Kanoa writes for the asset's current run: the mode, order and item, counts, times, availability, performance and quality, the current state and shift. It's a primary source for dashboards and live operator visualization. Treat it as read-only.

How it's filled. Most tags come from the asset's current mode event, through system.kanoa.event.getAssetProductionEvents({'activeModeOnly': True}). The updateAssetOEETimer timer writes them every minute, and setting OEE/refresh to true writes them for this asset straight away. The state tags change with the Inputs/State tag, the shift tags come from updateAssetShiftTimer every minute, and the schedule tags are written when the schedule starts or ends a run.

Totals for the current run. Times, counts and the OEE factors cover the period from when the current mode started (startDate) until now, not the shift or the day. They reset when the mode changes. In any mode other than Production they are blank, apart from durationSecs.

Run and timing​

TagDescription
startDateWhen the asset's current mode started
endDateNot written by Kanoa. The current mode has no end yet, so it stays blank
durationSecsSeconds from startDate until now, including downtime
runtimeSecsSeconds the asset spent in a Running state since startDate
unplannedDowntimeSecsSeconds the asset spent in an Unplanned Downtime state since startDate
plannedDowntimeSecsSeconds the asset spent in a Planned Downtime state since startDate

Counts​

TagDescription
infeedCountTotal of the asset's infeed counters since startDate. With no infeed counts it's derived as outfeedCount × packageCount + wasteCount
outfeedCountTotal of the asset's outfeed counters since startDate. With no outfeed counts it's derived as (infeedCount − wasteCount) ÷ packageCount
wasteCountTotal of the asset's waste counters since startDate. With no waste counts it's derived as infeedCount − outfeedCount × packageCount, or 0 when only one count is connected
targetCountHow many the asset should have made in its Running time at the standard rate: runtimeSecs × standardRate, per hour or per minute depending on period
packageCountHow many infeed units make one outfeed unit, from the item's production rate on this asset (1 when not set)
infeedUnits, outfeedUnits, wasteUnitsThe units each count is in, from the item's production rate on this asset

OEE factors and rates​

TagDescription
availabilityRunning time as a share of the time the asset should have been running: runtimeSecs ÷ (runtimeSecs + unplannedDowntimeSecs). Planned downtime isn't counted against it
performanceActual output against the standard rate during Running time: infeedCount ÷ (standardRate × Running time in the rate's period). For a Batch (in mins) rate it's standardRate × 60 ÷ runtimeSecs. 0 when there's no infeed, no Running time or no standard rate
qualityShare of the infeed that wasn't waste: (infeedCount − wasteCount) ÷ infeedCount. 1 when there's no infeed yet
standardRateThe rate performance is measured against: the rate set on the run if one was given, otherwise the item's standard rate on this asset
periodWhat standardRate is per: Minute, Hour or Batch (in mins)
infeedRateSecsThe average infeed count per second of Running time since startDate. It isn't an instantaneous rate, and downtime doesn't count towards the time
outfeedRatePerMinuteThe average outfeed count per minute since startDate: outfeedCount ÷ (durationSecs ÷ 60). Unlike infeedRateSecs, downtime does count towards the time

Mode​

TagDescription
modeId, modeNameThe asset's current mode, for example Production or Idle. When Enable Quality is on, a change of modeId triggers Kanoa Quality's mode-change checks
modeTypeNameThe current mode's type
modeColor, iconPathThe current mode's color and icon, for screens
modeSourceNameWhat started the current mode: PLC, Operator, Operator Run or Schedule
userNameThe user who started the current mode

Order and item​

TagDescription
workOrderId, workOrderNameThe production order running, if any
workOrderTitleThe order's title, or its name when it has no title
reqdQtyThe order's required quantity
woActualQtyOutfeed counted against the order so far, across its production runs in the last month, including counts recorded against the order on other runs
woRemainingMinutesEstimated minutes to finish the order at the current pace: (reqdQty − woActualQty) ÷ outfeedRatePerMinute. Blank when there's no order, required quantity, actual quantity or outfeed rate yet
itemId, itemName, itemDescriptionThe item running. When Enable Quality is on, a change of itemId triggers Kanoa Quality's product-change checks

State​

Written by the Inputs/State tag each time the state changes.

TagDescription
stateNameThe current state's name, or the state code itself when that code isn't configured for the asset
stateTypeNameThe current state's type: Running, Unplanned Downtime or Planned Downtime
stateColorThe state's color, or its state type's color when the state has none
stateTypeColorThe state type's color
stateIconPathThe state's icon

Shift​

Written by the updateAssetShiftTimer timer every minute.

TagDescription
shiftId, shiftNameThe shift the asset is in now
shiftNameIdThe shift name's id. When Enable Quality is on, a change triggers Kanoa Quality's shift-change checks
shiftStart, shiftEndWhen the current shift started and when it ends

Schedule​

Written by the checkAssetScheduleTimer timer when the schedule starts a run on the asset, and cleared when the schedule ends it. Blank when the current run wasn't started by the schedule.

TagDescription
scheduleBlockIdThe schedule block that started the current run
scheduleStart, scheduleEndThe scheduled start and end of that block

Refresh​

TagDescription
refreshSet to true to write this asset's mode, order, item, count and OEE values now instead of waiting for the next timer run. It resets itself

OEE Thresholds Folder​

The OEE Thresholds folder stores the asset's OEE targets and tolerances (a, p, q, oee and their tolerances), used by dashboards and visual indicators. They are configured in Kanoa and loaded into the tags; set oeeThresholds/update to true to reload them. Treat them as read-only.

Import the CoreForge Simulator tags
caution

The Kanoa MES module must be installed before you start. It provides the Kanoa/timer and Kanoa/assetInputs UDTs that the simulator tags use. To check, look under UDT Definitions → Kanoa in the [Kanoa] provider and confirm timer and assetInputs are there.

  1. Download CoreForge Sim_UDTs_and_Tags.json
  2. In the Designer, open the Tag Browser and select the [Kanoa] tag provider.
  3. Select Import Tags by clicking on the three dots to the right of the Kanoa Tag provider and choose CoreForge Sim (UDTs + Tags).json using the Direct method. If asked, set Collision Policy to Overwrite, then click Import.
caution

Import from the top-level Tags folder of the [Kanoa] provider.

Import TagsImport Root folder

If you import into a subfolder, the simulator UDTs won't be created as UDT definitions, and every simulator tag will fail with "does not have item 'timer' for overrides". If you see this error, the simulator UDTs weren't created in the right place. Delete any stray types folder under Tags, then repeat the import from the top-level Tags folder with Overwrite. Your existing tags are repaired in place.

  1. Check the result:
    • UDT Definitions → custom contains simState and simCounter.
    • Tags → CoreForge Sim → Site contains the Machining, Mixing, Finishing and Coating assets.
    • Expand any asset's sim/state. It should show timer, values and simParams with no error icon.
  2. Expand the CoreForge tags (not the CoreForgeSim tags) and set the MQTT path parameter on each asset's line tag (assetOEE UDT instance) to the matching CoreForgeSim tag path:
Line UDTMQTT path value
Machine 1[Kanoa]CoreForge Sim/Site/Machining/Machine 1/assetInputs
Spray Line 1[Kanoa]CoreForge Sim/Site/Coating/Spray Line 1/assetInputs
Manual Pack 1[Kanoa]CoreForge Sim/Site/Finishing/Manual Pack 1/assetInputs

Your setup should look like something below...

Set Up Simulation
info

For our training, we set the MQTTPath parameter of the line UDT instance and we can do this because our simulator tags are named identical to the tags in the Inputs folder i.e `Infeed Count', 'State'. This allows us to set the path once. In reality, you'll be connecting to different named plc or mqtt tags for the source of counts and states, in which case you can simply change the reference of the Inputs tags for Infeed Count, Outfeed Count, Waste Count and State.

Check your work

Copy this script into the Ignition Designer Script Console (Tools → Script Console) and run it. It checks that both files were imported into the Kanoa provider, that the simulator tags are running, and that each line's assetOEE UDT has the right MQTT path. Each check prints PASS or FAIL with what to fix, plus a NOTE showing whether the line's inputs are reading the simulator. It doesn't change anything.

Show the check script
checkLesson-connectTags.py
# Kanoa Ops Training - check "Connect To Your Tags" (Base Configuration, lesson 7)
#
# Run in the Ignition Designer Script Console (Tools > Script Console), in the
# project that inherits KANOA_SCRIPTS. It only reads; it changes nothing.
# Every check prints PASS or FAIL with what to fix, then a summary.

PROVIDER = '[Kanoa]'
SIM_ROOT = PROVIDER + 'CoreForge Sim'
UDT_TYPES = ['custom/simState', 'custom/simCounter']
LINES = [
# asset path, MQTT path, a simulator tag that only works if the sim UDTs imported
('CoreForge\\Philadelphia\\Machining\\Machine 1',
SIM_ROOT + '/Site/Machining/Machine 1/assetInputs',
SIM_ROOT + '/Site/Machining/Machine 1/sim/state/values/stateCode'),
('CoreForge\\Philadelphia\\Coating\\Spray Line 1',
SIM_ROOT + '/Site/Coating/Spray Line 1/assetInputs',
SIM_ROOT + '/Site/Coating/Spray Line 1/Infeed/sim/infeed/values/countValue'),
('CoreForge\\Philadelphia\\Finishing\\Manual Pack 1',
SIM_ROOT + '/Site/Finishing/Manual Pack 1/assetInputs',
None),
]

results = []

def check(ok, what, fix=None):
results.append(bool(ok))
if ok:
print 'PASS %s' % what
else:
print 'FAIL %s' % what
if fix:
print ' -> %s' % fix

def note(what):
print 'NOTE %s' % what

def text(v):
return '' if v is None else unicode(v).strip()

def normTag(path):
# tag paths are case-insensitive; [~] means the tag's own provider
p = text(path).replace('\\', '/').rstrip('/').lower()
if p.startswith('[~]'):
p = PROVIDER.lower() + p[3:]
return p

def exists(path):
try:
return bool(path) and system.tag.exists(path)
except:
return False

def isGood(qv):
try:
return qv.quality.isGood()
except:
return False

def unwrap(v):
# getConfiguration parameter values can be wrapped in a dict or property object
for _ in range(3):
if v is None:
return None
if hasattr(v, 'get') and not isinstance(v, basestring):
try:
inner = v.get('value')
except:
inner = None
if inner is None:
return v
v = inner
elif hasattr(v, 'getValue'):
v = v.getValue()
else:
return v
return v

def mqttPath(udtPath):
qv = system.tag.readBlocking([udtPath + '/Parameters.MQTTPath'])[0]
if isGood(qv):
return text(qv.value)
try:
params = system.tag.getConfiguration(udtPath, False)[0].get('parameters') or {}
for k in params.keys():
if str(k).lower() == 'mqttpath':
return text(unwrap(params[k]))
except:
pass
return None

print '=' * 70
print 'Kanoa Ops Training - Connect To Your Tags check'
print '=' * 70

# 1. Simulator UDT definitions (simulator UDTs.json)
print '\n-- Simulator UDT definitions'
for t in UDT_TYPES:
check(exists(PROVIDER + '_types_/' + t), 'UDT definition %s exists in the Kanoa provider' % t,
'In the Tag Browser, select the Kanoa provider, click Import Tags, and import simulator UDTs.json.')

# 2. Simulation tag instances (CoreForge Sim Tags.json)
print '\n-- CoreForge Sim tags'
rootOk = exists(SIM_ROOT)
check(rootOk, 'CoreForge Sim folder is at the root of the Kanoa provider',
'Import CoreForge Sim Tags.json into the Kanoa provider with nothing selected, so the folder lands at the root '
'(not inside another folder, and not in the default provider).')
for assetPath, source, simTag in LINES:
name = assetPath.split('\\')[-1]
if rootOk:
check(exists(source + '/Inputs/State'), 'Simulator inputs exist for %s' % name,
'%s is missing. Re-import CoreForge Sim Tags.json.' % source)
if simTag and exists(simTag):
qv = system.tag.readBlocking([simTag])[0]
check(isGood(qv), '%s simulator tags are running' % name,
'%s reads %s. Import simulator UDTs.json (the definitions), then re-import CoreForge Sim Tags.json.'
% (simTag, qv.quality))

# 3. MQTT path on each line's assetOEE UDT
print '\n-- MQTT path on each assetOEE UDT'
for assetPath, source, simTag in LINES:
name = assetPath.split('\\')[-1]
try:
base = text(system.kanoa.asset.getAssetTagPath({'assetPath': assetPath}))
except Exception:
base = ''
udt = None
for c in (base + '/Line', base):
if base and exists(c + '/Inputs/State'):
udt = c
break
check(udt is not None, '%s has an assetOEE UDT' % name,
'No assetOEE tag found for %s. In Asset Configuration, make sure %s exists and is OEE Enabled.' % (assetPath, name))
if udt is None:
continue
actual = mqttPath(udt)
if not actual:
check(False, '%s MQTT path is set' % name,
'In the Tag Browser, edit %s and set the MQTT path parameter to %s' % (udt, source))
continue
if normTag(actual) == normTag(source):
check(True, '%s MQTT path is %s' % (name, source))
elif not actual.startswith('['):
check(False, '%s MQTT path includes the tag provider' % name,
"It's '%s'. Add the provider at the start: %s" % (actual, source))
else:
check(False, '%s MQTT path is %s' % (name, source),
"It's '%s'. Edit %s and set the MQTT path parameter to %s" % (actual, udt, source))
continue

# 4. Data reaches the assetOEE Inputs (informational: values move every tick)
pairs = ['Inputs/State', 'Inputs/Infeed Count', 'Inputs/Outfeed Count']
vals = system.tag.readBlocking([udt + '/' + p for p in pairs] + [source + '/' + p for p in pairs])
same = all(text(vals[i].value) == text(vals[i + len(pairs)].value) for i in range(len(pairs)))
if same:
note('%s assetOEE Inputs match the simulator (State %s, Infeed %s, Outfeed %s)'
% (name, text(vals[0].value) or '-', text(vals[1].value) or '-', text(vals[2].value) or '-'))
else:
note('%s assetOEE Inputs differ from the simulator right now (State %s vs %s). '
'Counts move every minute; re-run in a minute if this persists.'
% (name, text(vals[0].value) or '-', text(vals[len(pairs)].value) or '-'))

passed = results.count(True)
print '\n' + '=' * 70
print 'Result: %d of %d checks passed' % (passed, len(results))
if passed == len(results):
print 'All good. Your tags are connected.'
else:
print 'Fix the FAIL items above, then run this again.'
print '=' * 70