Configure States
A state represents equipment feedback. It answers questions such as: Is the equipment running? If not, why not? Is the asset faulted, idle, down, or producing? States are the foundation for downtime tracking and availability analysis.
-
Navigate to Configuration → Ops → Assets.
-
Select the States tab.
-
Select Add State Category (folder icon).
-
Add the following new state categories:
Mechanical├── Nozzle└── PumpProcess Failure
- Select the State Editor tab.
- Select the
Mechanical\Nozzlestate category and addSpray Nozzle Clogas Unplanned Downtime, andCleaningas Planned Downtime. - Select the
Mechanical\Pumpstate category and addPump Pressure Faultas Unplanned Downtime. - Select the
Process Failurestate category and addTemperature Out of Rangeas Unplanned Downtime. - Select the Asset Links tab.
- Select
CoreForge\Philadelphia\Coating\Spray Line 1and, holding the CTRL key, also select these new states. Click the Link button.
Copy this script into the Ignition Designer Script Console (Tools → Script Console) and run it. It checks the four state categories, that each new state is in the right category with the right state type, and that all four are linked to Spray Line 1. Each check prints PASS or FAIL with what to fix. It doesn't change anything.
Show the check script
# Kanoa Ops Training - check "Configure States" (Base Configuration, lesson 6)
#
# 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.
CATEGORIES = ['Mechanical', 'Mechanical\\Nozzle', 'Mechanical\\Pump', 'Process Failure']
STATES = [
# state, category, state type
('Spray Nozzle Clog', 'Mechanical\\Nozzle', 'Unplanned Downtime'),
('Cleaning', 'Mechanical\\Nozzle', 'Planned Downtime'),
('Pump Pressure Fault', 'Mechanical\\Pump', 'Unplanned Downtime'),
('Temperature Out of Range', 'Process Failure', 'Unplanned Downtime'),
]
SPRAY_LINE = 'CoreForge\\Philadelphia\\Coating\\Spray Line 1'
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 field(row, *names):
for n in names:
if n in row and row[n] is not None:
return row[n]
return None
def truthy(v):
return str(v).lower() in ('true', '1', 'yes')
def text(v):
return '' if v is None else unicode(v).strip()
def norm(path):
# accept either separator in category paths
return text(path).replace('/', '\\').strip('\\')
def rows(fn, params):
return system.kanoa.utilities.convertDatasetToJSON(fn(params))
print '=' * 70
print 'Kanoa Ops Training - Configure States check'
print '=' * 70
# 1. State categories
print '\n-- State categories'
categories = set(norm(field(r, 'stateCategoryPath')) for r in rows(system.kanoa.asset.getStateCategory, {}))
lowerCats = dict((c.lower(), c) for c in categories)
for path in CATEGORIES:
if path in categories:
check(True, 'State category %s exists' % path)
elif path.lower() in lowerCats:
check(False, 'State category %s exists' % path,
"Found '%s'. Rename it to match exactly (names are case-sensitive)." % lowerCats[path.lower()])
else:
parent = '\\'.join(path.split('\\')[:-1])
check(False, 'State category %s exists' % path,
'On the States tab, click Add State Category and add %s%s.' % (path.split('\\')[-1], (' under ' + parent) if parent else ''))
# 2. States
print '\n-- States'
states = rows(system.kanoa.asset.getStates, {})
byName = {}
for r in states:
byName.setdefault(text(field(r, 'stateName')), []).append(r)
for name, category, stateType in STATES:
found = byName.get(name, [])
if found:
check(True, 'State %s exists' % name)
else:
near = [n for n in byName if ' '.join(n.lower().split()) == name.lower()]
if not near:
check(False, 'State %s exists' % name,
'On the State Editor tab, select %s and add %s as %s.' % (category, name, stateType))
continue
check(False, 'State %s exists' % name,
"Found '%s'. Rename it to '%s' (names are case-sensitive)." % (near[0], name))
# keep checking the misnamed state so every other problem shows in one run
found = byName[near[0]]
byName[name] = found
inCategory = [r for r in found if norm(field(r, 'stateCategoryPath')) == category]
if not inCategory:
where = ', '.join(sorted(set(norm(field(r, 'stateCategoryPath')) or 'no category' for r in found)))
check(False, '%s is in category %s' % (name, category),
"It's in %s. Move it to %s, or delete it and add it again under %s." % (where, category, category))
row = found[0]
else:
check(True, '%s is in category %s' % (name, category))
row = inCategory[0]
actualType = text(field(row, 'stateTypeName'))
check(actualType.lower() == stateType.lower(), '%s is %s' % (name, stateType),
"It's '%s'. Edit the state and set its type to %s." % (actualType or 'blank', stateType))
enabled = field(row, 'enabled', 'stateEnabled')
if enabled is not None and not truthy(enabled):
check(False, '%s is enabled' % name, 'Edit the state and enable it.')
# 3. Links to Spray Line 1
print '\n-- States linked to Spray Line 1'
links = rows(system.kanoa.asset.getAssetStates, {'assetPath': SPRAY_LINE})
links = [r for r in links if text(field(r, 'assetPath')) in ('', SPRAY_LINE)]
def squash(v):
# compare names ignoring case, extra spaces and any category prefix
return ' '.join(text(v).split('\\')[-1].lower().split())
for name, category, stateType in STATES:
ids = set(str(field(r, 'stateId')) for r in byName.get(name, []) if field(r, 'stateId') is not None)
matches = [r for r in links
if (ids and str(field(r, 'stateId')) in ids)
or text(field(r, 'stateName')) == name
or squash(field(r, 'stateName', 'statePath')) == squash(name)]
enabledLinks = [r for r in matches if field(r, 'linkEnabled') is None or truthy(field(r, 'linkEnabled'))]
if enabledLinks:
check(True, '%s is linked to Spray Line 1' % name)
elif matches:
check(False, '%s is linked to Spray Line 1' % name,
'The link exists but is disabled. On the Asset Links tab, select Spray Line 1 and enable the %s link.' % name)
else:
check(False, '%s is linked to Spray Line 1' % name,
'On the Asset Links tab, select Spray Line 1, CTRL-click %s, and click Link.' % name)
# show what Spray Line 1 does have, to make a mismatch easy to spot
have = sorted(set('%s (id %s)' % (text(field(r, 'stateName')), field(r, 'stateId')) for r in links))
print ' Spray Line 1 has these states linked: %s' % ', '.join(have)
print ' %s has state id(s): %s' % (name, ', '.join(sorted(ids)) or 'none found')
passed = sum(1 for r in results if r)
print '\n' + '=' * 70
if passed == len(results):
print 'All %d checks passed. Configure States is complete.' % len(results)
else:
print '%d of %d checks passed. Fix the FAIL items above and run this again.' % (passed, len(results))
print '=' * 70
If you want to check your work, or skip ahead, you can import the updated asset hierarchy instead of building it by hand.
Downloadcoreforge-asset-model-states-export-4.jsonImport it on the asset configuration screen (Configuration → Ops → Assets). See Creating Assets.

