T13: OEE
Availability times Performance times Quality — and the definitional arguments that decide whether anyone believes the number.
What you will build: a complete OEE calculation with its three components, and the manual inputs that make it defensible.
Time: about 45 minutes.
You need: T07 (events) and T10 (KPI layers) completed.
Step 1 — The definitions matter more than the arithmetic#
OEE is:
OEE = Availability × Performance × Quality
The arithmetic is trivial. The reason OEE projects fail is that the three components are defined differently by different people, and nobody agrees which number is right.
Before building anything, settle these with the plant:
| Question | Why it matters |
|---|---|
| What counts as scheduled time? | Availability's denominator. Calendar time? Planned production time? Excluding planned maintenance? |
| Is planned downtime a loss? | Including it makes availability much lower and arguably more honest |
| What is the ideal rate? | Performance's reference. Nameplate? Best demonstrated? Per product? |
| Does rework count as good? | Quality's numerator |
| Does the rate differ by product? | If yes, performance needs the product in scope |
Write the definitions down before you build. An OEE number nobody agrees with is worse than no OEE number, because it generates arguments instead of improvement. Get the definitions agreed and recorded, and put them where the dashboard is — a Text panel stating "Availability excludes planned maintenance" prevents the same conversation every month.
For this tutorial: scheduled time excludes planned maintenance, the ideal rate is a manually entered figure, and rework does not count as good.
Step 2 — Manual inputs#
Two things must come from people. Under Filler 1, create a metric OEE Inputs:
| Measure | Retrieval type | Interval | Unit |
|---|---|---|---|
Planned Production Minutes |
Manually entered | Day(s) | min |
Ideal Rate |
Manually entered | Day(s) | bottles/min |
Put both on a form and enter values — say 1,320 minutes (22 hours, allowing 2 hours planned maintenance) and 60 bottles/min.
Making scheduled time a manual measure rather than assuming 1,440 minutes is what makes availability defensible. It is visible, adjustable, owned by a person, and versioned by period — so when someone questions the number you can show them the input.
Put limits on both. An ideal rate of 600 instead of 60 would silently make performance 10% of reality.
Step 3 — Availability#
Availability is running time as a share of scheduled time.
You have Downtime Minutes Daily from an event-aggregated measure using Active Duration. But that includes all stoppages, and you decided planned maintenance is excluded from scheduled time rather than counted as downtime.
So you need unplanned downtime specifically. Add a second event-aggregated measure filtered to unplanned reasons — or, more simply for now, add an attribute-driven split. Create:
| Measure | Interval | Notes |
|---|---|---|
Unplanned Downtime Daily |
Daily | Event-aggregated, Active Duration, on stoppages whose reason category is unplanned |
Then:
| Field | Value |
|---|---|
| Name | Availability Daily |
| Interval | Day(s) |
| Unit | % |
| Format | 0.0 '%' |
SafeDivide(
(Measure_<plannedMinutesId>.Value ?? 0) - (Measure_<unplannedDowntimeId>.Value ?? 0),
Measure_<plannedMinutesId>.Value) * 100
Active Duration is not optional here. If you use Event Duration (Start) instead, an eight-hour stoppage beginning at 22:00 attributes all eight hours to the day it started in. Availability then goes below zero on that day and above 100% on the next. Only Active Duration attributes the portion of each event that fell inside each interval, which is what makes the percentage meaningful.
If your availability is outside 0–100%, check the anchor before checking the arithmetic.
Step 4 — Performance#
Performance is actual output as a share of what could have been produced in the time the machine was actually running.
Performance = Actual Output / (Running Time × Ideal Rate)
First, running time. Add a calculated measure:
| Field | Value |
|---|---|
| Name | Running Minutes Daily |
| Interval | Day(s) |
| Unit | min |
Math.Max((Measure_<plannedMinutesId>.Value ?? 0) - (Measure_<unplannedDowntimeId>.Value ?? 0), 0)
The Math.Max guards against a negative, which would otherwise happen if downtime exceeded planned time — a data problem, but one you would rather not see propagate.
Then performance, using total output including rejects, since the machine did produce them:
| Field | Value |
|---|---|
| Name | Performance Daily |
| Interval | Day(s) |
| Unit | % |
| Format | 0.0 '%' |
SafeDivide(
(Measure_<bottlesDailyId>.Value ?? 0) + (Measure_<rejectsDailyId>.Value ?? 0),
(Measure_<runningMinutesId>.Value ?? 0) * (Measure_<idealRateId>.Value ?? 0)) * 100
Note that performance uses total output and quality uses good output. Using good output in both double-counts the quality loss, which is one of the commonest OEE errors.
Step 5 — Quality#
Good output as a share of total output:
| Field | Value |
|---|---|
| Name | Quality Daily |
| Interval | Day(s) |
| Unit | % |
| Format | 0.0 '%' |
SafeDivide(
Measure_<bottlesDailyId>.Value,
(Measure_<bottlesDailyId>.Value ?? 0) + (Measure_<rejectsDailyId>.Value ?? 0)) * 100
Step 6 — OEE#
| Field | Value |
|---|---|
| Name | OEE Daily |
| Interval | Day(s) |
| Unit | % |
| Format | 0.0 '%' |
(Measure_<availabilityId>.Value ?? 0) / 100
* (Measure_<performanceId>.Value ?? 0) / 100
* (Measure_<qualityId>.Value ?? 0) / 100
* 100
The divisions and final multiplication convert between percentage and ratio. You could store the three components as ratios instead and multiply directly — cleaner arithmetic, but percentages are what people want to read on a dashboard, so the conversion has to happen somewhere.
Step 7 — Sanity-check it#
Real OEE on a well-run line is typically 60–85%. If you are seeing something else, work through:
| Symptom | Cause |
|---|---|
| Above 100% | Ideal rate too low, or performance using the wrong output |
| Availability outside 0–100% | Wrong anchor — use Active Duration |
| Performance above 100% | Ideal rate too low, or running time overstated |
| Suspiciously high overall | Scheduled time too narrow, or downtime not all captured |
| Suspiciously low | Ideal rate too high, or planned time counted as downtime |
| Quality at exactly 100% always | Reject counter not connected |
Check each component separately before questioning OEE. That is why the layered approach from T10 matters — a composite number is only debuggable if its parts are visible.
Step 8 — Present the losses, not just the score#
An OEE score tells you how bad. It does not tell you what to fix, and on its own it generates no action.
Add to a dashboard:
- The three components as separate widgets, so the dominant loss is visible
- A Pareto of downtime by reason — see T17
- A trend of OEE over 30 days
The management question is "what is our OEE". The useful question is "which of the three is costing us most, and what specifically within it". A dashboard that answers only the first will be looked at once a month and acted on never.
Step 9 — Roll it up#
Aggregate the quantities to monthly, then recalculate the components from the monthly totals:
SafeDivide(
(Measure_<plannedMinutesMtdId>.Value ?? 0) - (Measure_<unplannedDowntimeMtdId>.Value ?? 0),
Measure_<plannedMinutesMtdId>.Value) * 100
Do not average the daily percentages. A monthly availability is total running time over total scheduled time. Averaging thirty daily figures weights a two-hour day the same as a full one, and produces a number that will not reconcile with the totals — which somebody will check.
What you have learned#
Definitions decide whether OEE is believed. Agree and record them before building.
Scheduled time and ideal rate belong in manual measures. Visible, adjustable, owned, versioned.
Active Duration is required for anything that becomes a time-based percentage.
Performance uses total output; quality uses good output. Using good in both double-counts.
Guard everything and use Math.Max where a negative would be nonsense.
Check the components, not the composite, when a number looks wrong.
Recalculate period ratios from period totals. Never average a ratio.
Present losses, not just the score.
Try this before moving on#
Make the ideal rate depend on the product.
A line runs slower on some products, so a single ideal rate makes performance look poor on slow products and excellent on fast ones. Use the production run event's product attribute to select the right rate.
This is where events, model attributes, and calculations come together — and it is the point at which an OEE model stops being an arithmetic exercise and starts reflecting the plant.
Next: T14: Multi-Interval Models — from minutely peaks to yearly rollups.