Operator Login and Analysis
Operator login is not the same as signing in to Kanoa. It records an operator badging in to the line they're working, and badging out when they go on break, finish their shift, or move to another line. These sessions are the raw presence data OLE is built from.
Badging into a new line automatically ends the operator's session on their previous line, so nobody appears to be working two lines because they forgot to badge out. A lead who really does cover two lines can be allowed multiple sessions.
Login time only counts inside the operator's shift assignment from the previous lesson. If you badge in outside it, for example at 8:00 PM against a 7:00 AM to 3:00 PM Day Shift, none of the time is counted and the operator leaderboard comes back empty. Before you start, check on Ops → Scheduling → Crews that Day Crew is assigned to a shift that covers now.
- Make sure Machine 1 is running in production, so there is runtime to measure against.
- Navigate to Ops → Operations → Operator Login.
- Badge
johnDoein to Machine 1. - Badge
marcoDiazin to Machine 1 as well. - After about 10 minutes, badge
johnDoeout. - Leave
marcoDiazlogged in for another 20 minutes or so. Machine 1's simulator drops into downtime states at random, so some of that time the operator may be present while the machine isn't running. - Review the sessions table and confirm the login and logout times.
Attendance compares these sessions with the crew's shift assignments from the previous lesson. Login time outside an operator's assigned window isn't counted, which is why the assignment should cover the time you're actually working. An operator with no assignment at all is measured against the whole report window instead.
You now have two people sharing one line for part of the time, one leaving early, and probably a stretch where someone was present but the machine wasn't running. Those are the situations OLE is designed to separate.
- Navigate to Ops → Analysis → Operators.
- Select Machine 1, today, and the EvenSplit attribution mode.
- Read the KPI tiles for the whole line. Hover each one to see what it measures:
- Attendance is well below 100%, because both operators were logged in for only part of their scheduled time, and
marcoDiazleft early. - Labor Gap compares OLE and POLE for the line. The tiles divide POLE by the report window rather than by scheduled labor time, so if Machine 1 ran most of the day while your operators were logged in for only half an hour, Labor Gap goes negative and is labeled Understaffed (see the tip below).
- Attendance is well below 100%, because both operators were logged in for only part of their scheduled time, and
- Review the Utilization breakdown bars. Asset time and labor time are shown separately, because a line's runtime and the people's presence are independent.
- In the Operator leaderboard, compare the two operators, ranked by POLE. For each operator, POLE is never higher than OLE. If Machine 1 went down while
johnDoewas still logged in, their POLE is lower than their OLE: that difference is present-but-not-producing time. - Change the asset to the Philadelphia site. The same numbers roll up across every line beneath it, without scheduling anything again.
- Click
operatorin the leaderboard to see their numbers broken down by each line they worked.
| Attribution mode | How a shared line's output is credited | Use it for |
|---|---|---|
| EvenSplit (default) | Shared between everyone logged in at the same time | Normal crew work |
| FullCredit | Everyone logged in gets full credit | Training and shadowing |
| PrimaryOnly | Only the first person logged in gets performance and quality credit | One operator running the line with helpers |
At the line or site level, Labor Gap can go negative. That means the line kept running near its normal rate even though fewer scheduled people showed up: understaffed, but holding for now. It's an early warning, and the dashboard labels it Understaffed rather than Present, not producing.
Run this in the Script Console. It lists today's sessions on Machine 1, checks each operator's login time against their shift assignment, then prints the leaderboard. If the leaderboard is empty, the FAIL lines above it say why.
Show the check script
# Kanoa Ops Training - OLE / POLE from a script (Operator effectiveness, lesson 2)
#
# Shows today's operator sessions on Machine 1, checks each one against that
# operator's shift assignments, then prints the operator leaderboard. Login time
# outside an operator's assigned window isn't counted, so the leaderboard can come
# back empty even though the sessions are there. It only reads; it changes nothing.
PATH = 'CoreForge\\Philadelphia\\Machining\\Machine 1'
MODE = 'EvenSplit' # or 'FullCredit', 'PrimaryOnly'
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 rows(data):
return system.kanoa.utilities.convertDatasetToJSON(data) if data is not None else []
def fmt(d):
return system.date.format(d, 'HH:mm') if d is not None else 'open'
def overlapSecs(s1, e1, s2, e2):
"""Seconds two periods overlap. An open end counts as now."""
e1, e2 = e1 or now, e2 or now
s = s1 if system.date.isAfter(s1, s2) else s2
e = e1 if system.date.isBefore(e1, e2) else e2
return max(0, system.date.secondsBetween(s, e))
now = system.date.now()
start = system.date.midnight(now)
assetId = system.kanoa.utilities.getFieldValue('assetId', system.kanoa.asset.getAssets({'assetPath': PATH}))
# Assignments on Machine 1 or anything above it (a crew scheduled at the site covers every line beneath it)
parts = PATH.split('\\')
coverIds = set()
for i in range(1, len(parts) + 1):
aid = system.kanoa.utilities.getFieldValue('assetId', system.kanoa.asset.getAssets({'assetPath': '\\'.join(parts[:i])}))
if aid is not None:
coverIds.add(aid)
print '=' * 70
print 'Operators on %s today (%s to %s)' % (parts[-1], fmt(start), fmt(now))
print '=' * 70
# 1. Sessions
print '\n-- Operator sessions'
sessions = rows(system.kanoa.operators.getOperators({'assetId': assetId, 'startDate': start, 'endDate': now}))
check(sessions, 'Operators badged in to %s today (%d sessions)' % (parts[-1], len(sessions)),
'In Ops > Operations > Operator Login, badge johnDoe and marcoDiaz in to Machine 1.')
for s in sessions:
print ' %-12s %s to %s' % (text(s.get('userName')), fmt(s.get('startDate')), fmt(s.get('endDate')))
# 2. Sessions vs shift assignments
assignments = [a for a in rows(system.kanoa.operators.getOperatorShiftAssignments({'startDate': start, 'endDate': now}))
if a.get('assetId') in coverIds]
if sessions:
print '\n-- Logins inside the scheduled window'
for userId in sorted(set(s.get('userId') for s in sessions)):
mine = [s for s in sessions if s.get('userId') == userId]
name = text(mine[0].get('userName'))
sched = [a for a in assignments if a.get('userId') == userId]
windows = ', '.join('%s to %s' % (fmt(a.get('startDate')), fmt(a.get('endDate'))) for a in sched)
if not sched:
note('%s has no shift assignment today, so they are measured against the whole report window. '
'Their attendance will look low; that is expected.' % name)
continue
counted = sum(overlapSecs(s.get('startDate'), s.get('endDate'), a.get('startDate'), a.get('endDate'))
for s in mine for a in sched)
check(counted > 0, '%s logged in during their scheduled window (%d min counted, scheduled %s)' % (name, counted // 60, windows),
'None of their login time falls inside %s, so none of it is counted and they drop off the leaderboard. '
'On Ops > Scheduling > Crews, assign Day Crew to the shift you are working now, then run this again.' % windows)
# 3. The leaderboard
print '\n-- Operator leaderboard (%s)' % MODE
board = rows(system.kanoa.operators.getOLEOperatorLeaderboard(assetId, start, now, MODE))
check(board, 'The leaderboard has %d operator(s)' % len(board),
'No counted login time yet. Fix any FAIL above; with no FAIL, badge an operator in and wait a few minutes.')
for r in board:
print ' ' + ', '.join('%s=%s' % (k, text(r[k])) for k in sorted(r.keys()))
if board and all(not r.get('ole') and not r.get('pole') for r in board):
note('OLE and POLE are 0. Check Machine 1 has a standard rate for the item it is running, and that it is in production.')
passed = results.count(True)
print '\n' + '=' * 70
print 'Result: %d of %d checks passed' % (passed, len(results))
print '=' * 70
If OLE and POLE come back as 0 while attendance looks right, check that Machine 1 has a standard rate for the item it's running. Performance, and so OLE and POLE, can't be calculated without one.