OEE Calculation: Overall Equipment Effectiveness Architecture from IIoT Data, MES and ISA-95 (2026)
Two plants report 72 percent OEE for the same kind of filling line. One computed it from a clipboard and a daily spreadsheet. The other computed it from a state machine on the PLC, a stream processor and a time-in-state ledger. The first number is a rumour with a decimal point; the second is a measurement. The difference is not the formula, which fits on a napkin, but everything upstream of it: what counts as “running”, whose clock stamps the event, and what happens to a three-second stop that nobody logs.
A sound OEE calculation multiplies three ratios, availability, performance and quality, but each ratio is only as honest as the data model underneath it. In an IIoT architecture that means a machine state model with reason codes, counters that survive rollover, an ideal cycle time that someone owns, and an event-driven pipeline that integrates time in state instead of sampling it.
What this covers: the OEE definition and a worked example, how ISO 22400 and the PackML state model constrain the vocabulary, the data sources (PLC counters, OPC UA, MTConnect, MQTT Sparkplug), a reference architecture for event-driven calculation, planned versus unplanned time rules, ideal cycle time pitfalls, TEEP, ISA-95 placement, data quality failure modes, a SQL and Python implementation, and how to keep dashboards honest.
Context and Background
Overall equipment effectiveness (OEE) is a single percentage that answers one question: of the time we could have spent making good product at the best demonstrated speed, how much did we actually spend doing so? It comes from the total productive maintenance (TPM) tradition associated with Seiichi Nakajima, and it classifies lost time into the “six big losses”: breakdowns, setup and adjustment, minor stops and idling, reduced speed, process defects, and startup or yield losses. Each loss lands in exactly one of three buckets. Breakdowns and setups reduce availability, minor stops and slow running reduce performance, and scrap and rework reduce quality.
The method became popular because it is cheap to explain. It became unreliable for the same reason. A plant manager can say “we are at 65 percent” without anyone specifying the planned production time, the ideal cycle time or the treatment of changeovers. Two sites that disagree on any of those three inputs are not comparing the same quantity, and benchmarking against a “world-class 85 percent” figure, commonly quoted as roughly 90 percent availability, 95 percent performance and 99 percent quality (the product is about 85 percent), is meaningless unless the definitions match. Treat that figure as folklore from a particular era and industry rather than a universal target.
Standards exist to remove the ambiguity. ISO 22400, “Automation systems and integration: Key performance indicators for manufacturing operations management”, defines a set of manufacturing KPIs, including OEE and its factors, in terms of explicit time elements such as planned busy time, actual unit busy time and actual unit downtime. I could not retrieve the normative text for this article, so where I describe the ISO structure below I stay at the level of the named time elements and recommend you check the clause wording of ISO 22400-2 before encoding it in a contract or a compliance document.
The second source of structure is the machine itself. The ISA-TR88.00.02 technical report, better known as PackML, defines a standard state model for packaging machines. Ten acting states (Starting, Stopping, Aborting, Clearing, Suspending, Unsuspending, Holding, Unholding, Resetting, Completing) and six wait states (Idle, Stopped, Suspended, Held, Complete, Aborted) surround a single true running state, Execute. Mapping a machine to a shared model is what lets an OEE engine treat a filler, a labeller and a palletizer uniformly. OPC Foundation also publishes a PackML companion specification that exposes this model over OPC UA, which we return to later.
If you want the broader architectural frame in which an OEE service sits, our guide to ISA-95 and ISA-99 standards covers the functional hierarchy and security zones, and our comparison of MTConnect and OPC UA for machine tool data covers the shop-floor data layer that feeds it.
The thesis of this post is narrow and practical. OEE is not a dashboard feature. It is a derived quantity computed from a time ledger, and the quality of that ledger, its state taxonomy, its clocks and its ownership of ideal cycle time, decides whether the number can be trusted, audited and acted on.
The OEE Formula: Availability, Performance and Quality
The standard OEE is availability times performance times quality, where availability is run time over planned production time, performance is actual output over the output achievable at ideal speed during run time, and quality is good output over total output. Each factor lies between zero and one, so OEE can never exceed its weakest factor.
The three ratios, defined carefully
Let the planned production time (PPT) be the time the asset was scheduled to produce, after removing time for which no demand or no staffing existed (lunch breaks, planned maintenance windows, unscheduled shifts). Let run time (RT) be PPT minus all stop time that occurred inside it, whatever the cause. Then:
- Availability = RT / PPT. It captures breakdowns, changeovers, material starvation and any stop long enough to be recorded as a stop.
- Performance = (Total count x Ideal cycle time) / RT. It captures slow cycles and micro-stops. Equivalently it is actual rate divided by ideal rate during run time.
- Quality = Good count / Total count. It captures scrap, rework (counted as not good on the first pass) and startup rejects.
Multiply the three and the intermediate terms cancel: OEE = (Good count x Ideal cycle time) / PPT. This identity is the most useful thing to remember about the metric. OEE is the fraction of planned time that was spent producing good parts at ideal speed, and that is exactly how a stream processor should compute it, as a sum of “fully productive seconds” over a window.
A worked example
Take a bottling line on an 8-hour shift, 480 minutes. Breaks are 30 minutes, planned, so PPT = 450 minutes. The line stops for a 20-minute changeover, a 25-minute breakdown, and 15 minutes waiting for caps: 60 minutes of stoppage. Run time is 390 minutes. The ideal cycle time is 1.0 second per bottle, which is 60 bottles a minute. The counter shows 19,890 bottles. 780 are rejected at the inspection station.
- Availability = 390 / 450 = 0.8667.
- Ideal output in 390 minutes = 390 x 60 = 23,400 bottles. Performance = 19,890 / 23,400 = 0.85.
- Quality = (19,890 – 780) / 19,890 = 19,110 / 19,890 = 0.9608.
- OEE = 0.8667 x 0.85 x 0.9608 = 0.7078, about 70.8 percent.
Check with the identity: good count x ideal cycle time = 19,110 seconds = 318.5 minutes; 318.5 / 450 = 0.7078. The two routes agree, which is a useful unit test for any implementation.
The example also shows what the number hides. The 30 percent loss splits into 13.3 points of availability loss, about 15 points of performance loss and about 4 points of quality loss, but in time terms the loss is 450 – 318.5 = 131.5 minutes: 60 minutes of stoppage, 58.5 minutes of slow running (390 – 331.5 minutes of equivalent ideal-speed production), and about 13 minutes of scrap. Reporting the loss tree in minutes, not only the percentages, is what lets a maintenance planner compare a breakdown to a speed loss on the same axis.
What ISO 22400 adds
ISO 22400-2 expresses the same structure with named time elements, planned busy time, actual unit busy time and actual unit downtime, and defines availability, performance rate (called effectiveness in some formulations) and quality ratio as ratios over them. The practical contribution is vocabulary discipline. Your data model should store those time elements as explicit, queryable intervals, not as pre-aggregated percentages, so that the same ledger can answer questions in any of the standard’s terms. I do not reproduce its numerators and denominators here because I could not verify the clause text; the structure above is the widely used form and matches the standard’s intent as publicly described, but confirm before certifying.
Reference Architecture for Event-Driven OEE Calculation
A reliable OEE architecture captures state changes and counters at the edge with source timestamps, transports them as events, integrates time in state in a stream processor, enriches the result with order, product and ideal cycle time context from the MES, and writes an interval ledger from which every dashboard query is derived. The ledger, not the percentage, is the product.

Figure 1: Reference architecture. Edge collection stamps events at the source, the stream processor integrates time in state, and the interval ledger feeds every report.
The diagram shows five layers. Machines and PLCs expose state, counters and reject signals. An edge gateway normalises them and publishes events over a broker. A stream processor turns events into closed intervals. The MES (manufacturing execution system) supplies the context that makes an interval meaningful: which work order, which product, which ideal cycle time, which shift calendar. Finally a time-series or relational store holds the ledger and serves dashboards, alerts and the ERP interface.
Why a time-in-state ledger beats polling
The tempting design samples a “running” bit every few seconds and counts the samples. It works until it does not. A one-second micro-stop between samples vanishes, a machine that flaps between states every 400 ms produces a random-looking duty cycle, and the sum of samples drifts away from wall-clock time whenever the poller stalls. Sampling error is also invisible: there is no way to tell from the output that a stall happened.
The event-driven alternative records each state transition with its source timestamp and derives durations from the difference between consecutive events. Each closed interval has a start, an end, a state, a reason code and a machine identifier. Availability and the stoppage portion of the loss tree are then sums over intervals, exact to the timestamp resolution of the source. A stall in the pipeline delays results but does not corrupt them, because the durations come from the event times, not from when the processor happened to wake.
Interval storage also makes the metric re-computable. When the plant reclassifies a stop reason on Monday, you edit one interval and rerun the aggregation, and every downstream number stays consistent. With pre-aggregated percentages you can only overwrite history.
Data sources and what each gives you
Four source types dominate, and a real plant usually has all of them.
PLC counters and bits. The most direct source is the controller itself: a product counter, a reject counter, a run bit, perhaps a PackML state variable. Counters are monotonic integers that wrap at a fixed width, which matters later. The PLC can also timestamp transitions with its own clock at scan resolution, typically milliseconds, which is better than anything a downstream poller can reconstruct.
OPC UA. OPC UA (IEC 62541) exposes variables with a source timestamp and a server timestamp, and subscriptions deliver changes as they occur rather than on a poll. The OPC Foundation’s PackML companion specification models the PackML state machine as OPC UA types, including an execute-state machine type, so a compliant server presents state in a standard shape. The source timestamp is the one to store; the server timestamp only tells you when the gateway noticed.
MTConnect. For machine tools, MTConnect agents publish an execution state, a controller mode and counters such as parts produced as a stream of observations with sequence numbers. The sequence number is valuable: it lets a consumer detect that it missed observations after a restart and request exactly the gap. Our comparison of MTConnect versus OPC UA and the wider look at UMATI, MTConnect and OPC UA cover when each fits.
MQTT with Sparkplug B. Many plants move edge data over MQTT, and Sparkplug B adds a defined topic namespace, birth and death certificates, and a payload with per-metric timestamps. The birth/death mechanism helps OEE directly: a death certificate tells the consumer the edge node is gone, so the interval ledger can mark data as unknown rather than silently extending the last state. For the topic-design side, see our piece on NGSI-LD, FIWARE and the unified namespace.
Whatever the transport, three rules hold. Prefer source timestamps over arrival timestamps. Publish state changes and counter snapshots, not derived percentages. And keep the raw events for a defined retention period so the ledger can be rebuilt after a logic fix.
Stream processing: windows, keys and late data
Key the stream by machine identifier. For each key, hold the current open interval in state. On a state-change event, close the open interval at the event’s source time, emit it, and open a new one. On a counter snapshot, emit a delta against the previous snapshot into the same keyed state. A periodic “heartbeat” tick, say every minute, emits a partial interval so dashboards can show a running figure for an interval that has not yet closed.
Two subtleties deserve explicit design. First, late and out-of-order events: if an edge buffer flushes after a network outage, events arrive minutes late. Use event-time processing with an allowed lateness at least as long as your worst tolerated outage, and make interval emission idempotent by keying on machine, start time and state, so reprocessing replaces rather than duplicates. Second, shift and day boundaries: split intervals at calendar boundaries when you write them, so a stop that spans the end of a shift is attributed to both shifts in proportion, and shift-level OEE sums cleanly.
Frameworks such as Apache Flink, Kafka Streams or a simple keyed consumer on a partitioned log can all implement this; the choice matters less than the discipline of event-time keyed state. A modest plant with a few hundred machines producing a handful of events per minute each is a small workload, so favour whichever stack your team already operates.
The Machine State Model and Reason Codes
Availability is only as good as the definition of “stopped”, so the architecture needs a state model that is small, mutually exclusive and mapped from every machine type. A good model separates what the machine is doing from why it is not producing, using a short list of base states plus a hierarchical reason code that an operator or the machine itself assigns.

Figure 2: Mapping PackML states to OEE time categories. Execute is the only productive state; the rest are classified by planned status and cause.
Mapping PackML states to OEE categories
PackML’s Execute state is the only one in which the machine is making product in its normal mode, so it is the natural candidate for productive time. The other states split into groups that matter for classification:
- Idle and Complete are wait states in which the machine is healthy but not producing, typically because the order is done or the machine is waiting for a start command. Whether this is availability loss depends on whether the time is inside planned production time. If a downstream constraint is the reason, many plants classify it as “no demand” and exclude it from PPT; if the cause is an upstream material shortage, it is a loss.
- Held and Suspended are the interesting pair. Held generally means the machine has been paused because of an internal condition, for example a local fault that stopped the cycle. Suspended generally means the machine has paused because of an external condition, such as a blocked downstream machine or starved upstream. That distinction maps naturally onto “equipment fault” versus “line-balance loss” and is worth preserving in the reason code.
- Stopped and Aborted are the two stable stop states; Aborted follows a fault or emergency stop and requires a clearing action. Both are unplanned by default unless a planned-downtime calendar entry covers them.
- The acting states (Starting, Resetting, Clearing, Holding and so on) are short transitions. Their duration is real lost time, but attributing it requires a rule. The most defensible rule assigns an acting state to the category of the stable state it leads towards, so a Starting that follows a changeover counts as changeover time.
PackML also defines unit modes such as Producing, Maintenance and Manual. A machine in Maintenance mode is not available for production by definition, so mode should be a separate dimension of the interval record that can override the state category. If your machines do not implement PackML, you do not need to retrofit it; you need a mapping table from each native state to one of your categories, owned in configuration, versioned, and applied by the stream processor rather than hard-coded into dashboards.
Reason codes: the taxonomy that makes the number actionable
Time-in-state tells you that you lost 60 minutes. A reason code tells you why. Design the code tree with three levels: a category aligned to the six big losses (equipment failure, setup and changeover, minor stop, reduced speed, process defect, startup loss), a subcategory (for example, “capper jam”), and an optional free-text note. Keep the first level short and fixed so that reports are comparable across lines, and let the lower levels vary by asset class.
Capture matters more than taxonomy. Machine-sourced reasons (a fault code from the PLC) are precise and free of human bias but incomplete: a conveyor may be starved without any fault being raised. Operator-sourced reasons cover the gaps but are slow and subject to convenience bias, where the easiest button wins. A practical pattern is to auto-fill the reason whenever the machine can determine it, prompt the operator only for stops over a threshold such as two to five minutes, and default everything else to a visible “unclassified” bucket whose size is itself a tracked KPI. If unclassified time exceeds, say, 10 percent of downtime, the OEE number is telling you more about the data than about the plant.
Micro-stops and the threshold problem
Short stops are where state models quietly fail. A stop of eight seconds is too brief for an operator to classify and, if the threshold for “a stop” is, say, 60 seconds, it is not a stop at all in the availability ratio. It is therefore absorbed into performance as reduced speed. That is a legitimate modelling choice, and it is actually how the original TPM classification treats minor stops, but it must be consistent. Choose one threshold per asset class, document it, and apply it in the pipeline: intervals shorter than the threshold are tagged “micro-stop” and counted in performance loss rather than availability loss. Changing the threshold changes the availability/performance split without changing OEE, which is why comparing availability figures across sites with different thresholds is misleading even when the OEE agrees.
Planned vs Unplanned Time, Ideal Cycle Time and TEEP
The three decisions that most change an OEE number are what counts as planned production time, what the ideal cycle time is, and which loss categories are allowed to leave the denominator. They are policy decisions with arithmetic consequences, and the architecture should make each one explicit and versioned.
Planned versus unplanned time
Planned production time starts from calendar time and subtracts time the asset was not expected to produce. Typical exclusions are scheduled breaks, planned preventive maintenance, and shifts that were never staffed. The contested items are changeovers, material shortages and “no order” time. A strict reading counts changeovers as availability loss, because the asset was scheduled and did not make product; this is what makes OEE useful for driving setup reduction (SMED). A lenient reading treats scheduled changeovers as planned downtime, which raises OEE and hides the lever that often matters most.
Whichever policy you pick, store the calendar as data, not logic. A shift calendar table with start, end and a category per window lets the stream processor intersect state intervals with planned windows, and lets you recompute under a different policy for comparison. Report both when policies differ across sites: “OEE (changeover counted as loss)” and “OEE (changeover planned)” side by side stops arguments.
Ideal cycle time is the most abused input
Performance depends on the ideal cycle time, and the ideal is usually a number someone typed in once. There are four common sources, each with a failure mode:
| Source of ideal cycle time | Typical origin | Failure mode |
|---|---|---|
| Nameplate speed | Machine vendor datasheet | Optimistic; the line never runs at it, so performance is permanently low |
| Engineered standard | Industrial engineering study or routing | Goes stale as product or tooling changes |
| Best demonstrated rate | Fastest sustained run in history | Drifts upward; one lucky hour can distort the baseline |
| Current order rate | Takes the scheduled rate for the order | Performance is trivially near 100 percent; hides real loss |
The design guidance is to treat ideal cycle time as master data attached to the product-and-machine pair (and sometimes to the tooling), with an owner, an effective-from date and a change log. The stream processor should look it up as of the interval start so that historical OEE is stable when someone updates a standard. Performance above 100 percent is a diagnostic rather than a triumph: it means the standard is wrong, the counter is double-counting, or the machine is running faster than its rated speed, and the pipeline should flag it instead of capping it silently.
A related trap is multi-product lines. When two SKUs share a machine at different speeds, a single machine-level ideal rate is wrong. Compute performance per interval using the SKU active in that interval (the MES work-order context provides it), then sum the ideal-time-equivalents. Averaging after the fact biases the result towards whichever SKU ran longest.
TEEP: the denominator outside planned time
Total effective equipment performance (TEEP) uses calendar time as the denominator. Define loading as planned production time divided by calendar time; then TEEP = loading x OEE. For a plant that runs 16 hours a day, five days a week, calendar loading is 80 of 168 hours, so loading is 0.476, and an OEE of 0.708 gives a TEEP of about 0.337.
TEEP answers a different question: how much of the asset’s theoretical capacity are we using at all? A site can have excellent OEE and a poor TEEP if it simply does not run the asset on nights and weekends. The right use is capacity planning. Before approving capital for a second line, compare TEEP with realistic loading, because an extra shift on the existing line may be cheaper than a new machine. Do not use TEEP as a shop-floor performance score; operators cannot influence the schedule.
Quality: first pass versus final yield
Quality in OEE is conventionally first-pass yield: parts good the first time, with rework counted as defects. If you count reworked parts as good, quality looks better but the cost and time of rework vanish from the metric. Where the process allows, track both: first-pass quality in the OEE and a separate rework ratio KPI. The counters needed are good count, reject count and, where relevant, rework count, and they should come from the inspection point, not be inferred as total minus a guess. Time alignment also matters: if inspection happens 20 minutes downstream, the rejects belong to the interval in which the parts were made, which requires lot or serial traceability rather than a naive same-window subtraction.
Where OEE Lives in the ISA-95 Hierarchy
ISA-95 (also published as IEC 62264) divides enterprise and control systems into levels. Level 0 is the physical process, Level 1 is sensing and actuation, Level 2 is supervisory control (SCADA, HMI), Level 3 is manufacturing operations management (MES and equivalents), and Level 4 is business planning (ERP). OEE data originates at levels 1 and 2, is computed or enriched at level 3 and is consumed at levels 3 and 4.

Figure 3: OEE across the ISA-95 hierarchy. Raw events enter at levels 1 and 2, intervals and KPIs are produced at level 3, and aggregates flow up to ERP.
Which level computes what
Level 1 should do the cheap, deterministic, latency-sensitive work: run the state machine, increment counters and stamp transitions. Do not compute OEE in the PLC; its job is to be a trustworthy sensor of state. Level 2 may hold local buffers and a historian that survives a network outage, and provides operator reason-code entry through the HMI. Level 3 is where the time ledger, the shift calendar and the product master data meet, so it is the natural home for the interval-building service and the KPI engine. The ISA-95 activity models name this territory: production performance analysis and production tracking are Level 3 functions, and OEE is a typical output of them.
Level 4 consumes aggregates, not intervals: OEE by line by week, loss minutes by category, maybe cost of lost output. The interface should carry enough metadata, the policy version, the time window and the unclassified share, for ERP or BI users to know what they are looking at. Pushing raw events to Level 4 invites every consumer to re-implement the calculation differently, which recreates the clipboard problem at scale.
Cloud and edge placement
Modern deployments often put the stream processor in the cloud or at a plant data centre rather than inside a monolithic MES. That is fine, provided the architecture keeps the interval ledger authoritative and the edge capable of buffering. Cloud IoT platforms provide ingestion and rules engines that can implement the same keyed logic, but the failure case to design for is the WAN link down for hours: the edge must store-and-forward events with source timestamps, and the processor must accept them late. Security zoning from ISA/IEC 62443, covered in our ISA-95 and ISA-99 guide, applies to the conduit between levels 2 and 3: outbound publish from the plant, no inbound control path from the analytics tier.
Digital twin angle
An OEE ledger is a good seed for an asset digital twin because it is already structured around the asset identity, its states and its context. A twin that knows an asset’s state history, ideal rates and reason codes can answer “what if” questions such as how much OEE moves if changeover time falls by a third, by replaying the ledger with modified durations. That is simulation on real intervals, which is more credible than a spreadsheet model.
Implementation: Computing OEE from Intervals
The implementation reduces to three steps: build intervals, intersect them with planned windows, and aggregate. The code below shows a minimal, verifiable version in Python and SQL. It is deliberately small so you can test it against the 70.8 percent worked example.
Python: interval builder and OEE calculator
from dataclasses import dataclass
from datetime import datetime, timedelta
PRODUCTIVE = {"EXECUTE"}
@dataclass
class Interval:
machine: str
start: datetime
end: datetime
state: str # e.g. EXECUTE, HELD, STOPPED
reason: str = ""
planned: bool = False # inside a planned-stop window
def build_intervals(events, machine, horizon_end):
"""events: list of (timestamp, state) sorted by source time."""
out = []
for (t0, s0), (t1, _) in zip(events, events[1:] + [(horizon_end, None)]):
if t1 > t0:
out.append(Interval(machine, t0, t1, s0))
return out
def counter_delta(prev, curr, width_bits=32):
"""Delta of a monotonic counter with rollover handling."""
mod = 1 << width_bits
return (curr - prev) % mod
def oee(intervals, total_count, reject_count, ideal_cycle_s):
ppt = sum((i.end - i.start).total_seconds()
for i in intervals if not i.planned)
run = sum((i.end - i.start).total_seconds()
for i in intervals if i.state in PRODUCTIVE and not i.planned)
avail = run / ppt if ppt else 0.0
perf = (total_count * ideal_cycle_s) / run if run else 0.0
qual = (total_count - reject_count) / total_count if total_count else 0.0
return {"availability": avail, "performance": perf,
"quality": qual, "oee": avail * perf * qual,
"perf_flag": perf > 1.0}
Feed it intervals totalling 450 planned minutes with 390 in EXECUTE, a total count of 19,890, 780 rejects and an ideal cycle of 1.0 second, and it returns an availability of 0.8667, performance of 0.85, quality of 0.9608 and OEE of 0.7078. The perf_flag field implements the earlier advice: surface impossible performance instead of hiding it. The counter_delta function handles wrap-around with modular arithmetic, which is correct as long as the counter wraps at most once between samples.
SQL: shift-level OEE from the ledger
-- Interval ledger: one row per closed state interval, split at shift boundaries
-- machine_interval(machine_id, shift_id, start_ts, end_ts, state, is_planned_stop)
-- production_count(machine_id, shift_id, total_count, reject_count, ideal_cycle_s)
WITH t AS (
SELECT machine_id, shift_id,
SUM(EXTRACT(EPOCH FROM end_ts - start_ts))
FILTER (WHERE NOT is_planned_stop) AS ppt_s,
SUM(EXTRACT(EPOCH FROM end_ts - start_ts))
FILTER (WHERE NOT is_planned_stop AND state = 'EXECUTE') AS run_s
FROM machine_interval
GROUP BY machine_id, shift_id
)
SELECT t.machine_id, t.shift_id,
t.run_s / NULLIF(t.ppt_s, 0) AS availability,
(p.total_count * p.ideal_cycle_s) / NULLIF(t.run_s, 0) AS performance,
(p.total_count - p.reject_count)::float
/ NULLIF(p.total_count, 0) AS quality,
((p.total_count - p.reject_count) * p.ideal_cycle_s)
/ NULLIF(t.ppt_s, 0) AS oee
FROM t JOIN production_count p USING (machine_id, shift_id);
The final column uses the identity from earlier, good count times ideal cycle over planned time, so it stays correct even when run time is zero. Note that the query joins counts by shift. If one product ran in the shift, a single ideal cycle works. For multi-SKU shifts you need a count table keyed by machine, shift and SKU, and the oee column becomes a sum of (good_count * ideal_cycle_s) across SKUs divided by planned time.
Aggregating correctly across machines and time
OEE does not average. The mean of two lines’ OEE is not the plant’s OEE unless both had equal planned time and equal weights. Aggregate by summing numerators and denominators: plant OEE is the sum of fully productive time (good count times ideal cycle) divided by the sum of planned production time. The same applies from shift to week. Averaging daily percentages gives a short, quiet day the same weight as a full one and will not reconcile with the ledger. Because the ledger stores seconds, correct roll-ups are a one-line change.
For bottleneck-driven plants, a plant-level OEE across all machines is also less informative than the OEE of the constraint. Losses on a non-constraint machine with spare capacity often cost nothing, so report loss minutes by machine and mark which one is the constraint, instead of reducing the plant to a single score.
Trade-offs, Gotchas, and What Goes Wrong
Most bad OEE numbers come from data defects, not from the formula. The failure modes below recur across plants, and each one has a detectable signature that a pipeline can check automatically.

Figure 4: Data quality failure modes and the validation gates that catch them before they reach the ledger.
Counter rollover and resets. A 16-bit counter wraps at 65,536. At 60 parts a minute that is about 18 hours, so a shift-level subtraction of start from end silently produces a negative or enormous delta. A PLC restart resets counters to zero, which looks like a rollover but is not. The defence is to compute deltas between consecutive snapshots, use modular arithmetic only when the previous value was near the top of the range and the new value is small, and treat any delta implying a rate far above the machine’s physical maximum as a reset or a glitch, quarantined for review rather than added.
Clock skew and timezones. Intervals computed from timestamps from machines whose clocks disagree by a few seconds produce small overlaps and gaps; disagreements of minutes produce nonsense. Synchronise PLCs and gateways with NTP or PTP, record both source and ingest time, and monitor the difference. Store everything in UTC and apply timezones only when mapping to shift calendars; daylight-saving transitions otherwise create a 23-hour or 25-hour shift that no one remembers to handle.
Manual overrides. Operators who can edit state or reason codes after the fact can also, innocently or not, reclassify a breakdown as “planned maintenance”. Make edits additive: the original interval remains in an append-only table, an override is a new record with user, time and justification, and reports show the proportion of edited time. This is cheap, and it changes behaviour.
Gaps and double counting. A dropped connection leaves a hole in the ledger. If the processor extends the last state across the hole, a 30-minute outage is counted as 30 minutes of running or of stopped time, depending on the last event. Mark gaps as an explicit “unknown” state, exclude them from both numerator and denominator when short, and flag the shift’s data completeness. Conversely, a redelivered MQTT message can double-count a counter delta unless processing is idempotent.
Ghost production and phantom stops. A counter that increments when a sensor sees a bouncing bottle or a conveyor with vibration inflates output. A run bit that drops for 50 milliseconds on every cycle produces a thousand phantom micro-stops. Debounce at the edge, and cross-check count against the product of ideal rate and run time: performance of 130 percent is an alarm, not good news.
Gaming and Goodhart effects
When OEE becomes a target it stops being a good measure. Predictable behaviours include: raising the ideal cycle time to a conservative value so performance looks fine, running slow-moving easy SKUs when the board is watched, scheduling changeovers into planned breaks, logging stops as “no demand”, and holding rejects at the end of the shift to be counted in the next. None of this requires bad faith; it follows the incentive.
Design the system so these moves are visible. Version the ideal cycle time master data and alert on changes. Report OEE alongside loss minutes by category, so a drop in recorded downtime without a matching rise in output stands out. Track the unclassified share and the edited share. Compare OEE with an independent signal such as energy per good unit or pallets shipped. And separate the audience: use the number for the improvement team’s prioritisation, not for individual appraisal.
What OEE does not capture
OEE measures losses at the asset level. It does not tell you whether you made the right thing, whether the asset was the constraint, or what the inventory consequences were. A line can post 90 percent OEE building product nobody ordered. It also ignores the cost dimension: a minute lost on a constraint line is worth far more than a minute on an unconstrained one. For cost and schedule questions use throughput accounting, schedule adherence and the other ISO 22400 KPIs alongside it. Finally, OEE rewards stable, high-volume discrete or batch operations best; in high-mix, low-volume or continuous-process settings the notion of an ideal cycle time is fuzzy, and you may be better served by a purpose-built yield or utilization KPI.
Practical Recommendations
Start with definitions, not dashboards. Write a one-page OEE policy that fixes planned production time, treatment of changeovers, micro-stop threshold, first-pass quality rule and ideal cycle time ownership. Put it in version control and reference its version in every report.
Next, build the ledger before the percentage. Capture state transitions and counters with source timestamps at the edge, buffer them for outages, and emit closed intervals from a keyed event-time stream processor. Keep raw events for replay. Compute OEE as a query over intervals, using the identity good count times ideal cycle over planned time as your regression test.
Then make the data defensible. Add validation gates for counter deltas and impossible performance, explicit unknown state for gaps, append-only overrides, and a tracked unclassified-time share. Treat the data-completeness score of each shift as part of the result.
Finally, use the number as a conversation starter. Show the loss tree in minutes, by the six big losses, and rank by recoverable time at the constraint. Pilot on one line for a few weeks, reconcile against a manual count, and only then roll out.
Checklist:
- [ ] OEE policy document with version number, owned by one named role
- [ ] Machine state mapping table in configuration (PackML or native to categories)
- [ ] Source timestamps captured at PLC, OPC UA or MTConnect level; NTP or PTP verified
- [ ] Counter delta logic handles rollover and resets; rate sanity check in place
- [ ] Ideal cycle time stored per product and machine with effective dates and change log
- [ ] Shift calendar and planned-stop windows stored as data, with timezone handling tested
- [ ] Interval ledger is append-only for overrides; edit share reported
- [ ] Gaps recorded as unknown, never extended
- [ ] Roll-ups sum numerators and denominators; no averaging of percentages
- [ ] Parallel manual count reconciled within an agreed tolerance before go-live
This post describes system and measurement design only. It is not a recommendation for any particular vendor or a statement about compliance with any specific standard edition; verify normative text against the published standards.
Frequently Asked Questions
What is the formula for OEE calculation?
OEE equals availability times performance times quality. Availability is run time divided by planned production time, performance is actual count times ideal cycle time divided by run time, and quality is good count divided by total count. Multiplying them simplifies to good count times ideal cycle time divided by planned production time. In the worked example above, 390 of 450 planned minutes ran, performance was 85 percent and quality 96.1 percent, giving about 70.8 percent.
What is a good OEE score?
A figure of around 85 percent, built from roughly 90 percent availability, 95 percent performance and 99 percent quality, is often quoted as world class, but it is a rule of thumb from discrete manufacturing and depends on how time and ideal rate are defined. Compare against your own baseline and against sites using the same definitions. Many real lines run well below 85 percent, and improvement is better measured by trend and loss minutes than by a universal target.
How is OEE different from TEEP?
OEE uses planned production time as its denominator, so it measures how well scheduled time was used. TEEP uses all calendar time, so it measures how much of the asset’s theoretical capacity is being used at all. TEEP equals loading times OEE, where loading is planned production time over calendar time. A line with excellent OEE that is idle every weekend has a low TEEP, which makes TEEP the better input for capacity and investment decisions.
How do you calculate OEE from PLC and IIoT data?
Capture state transitions and product and reject counters with source timestamps, publish them as events over OPC UA, MTConnect or MQTT Sparkplug, and have a stream processor close intervals per machine and state. Join each interval with shift calendar, work order and ideal cycle time, then sum productive and planned seconds and the count deltas. Compute OEE as a query on that ledger, and guard against rollover, clock skew and gaps.
Should planned downtime and changeovers count against OEE?
Planned breaks and maintenance windows are normally excluded from planned production time, so they do not reduce OEE. Changeovers are a policy choice. Counting them as availability loss highlights setup time and supports reduction work, while treating them as planned flatters the number. Whichever you choose, document it, version it, and ideally report both variants so comparisons across sites and over time remain honest.
Where does OEE sit in ISA-95?
Raw state and counters originate at Levels 1 and 2 (PLCs, SCADA), the interval ledger and KPI calculation belong at Level 3 (MES or manufacturing operations management), and aggregates flow to Level 4 (ERP and BI). Keep the PLC as a trustworthy source of state rather than computing OEE there, and send aggregates with policy metadata upward so consumers cannot re-derive the metric inconsistently.
Further Reading
- ISA-95 and ISA-99 standards: complete technical guide for the hierarchy and security zoning that frame where OEE is computed.
- UMATI vs MTConnect vs OPC UA for machine tools for the interoperability layer feeding machine state.
- NGSI-LD, FIWARE and the unified namespace for how to organise industrial data topics and context.
- MTConnect vs OPC UA for machine tool data for observation streams and sequence numbers.
- ISO 22400-2: KPIs for manufacturing operations management, the normative KPI definitions (paid standard).
- OPC Foundation PackML companion specification for the state machine as OPC UA types.
- ISA-88 and ISA-TR88.00.02 overview at ISA for the batch and PackML lineage.
By Riju — about
