Key takeaways
A plan that fires at 500 operating hours or 100,000 cycles needs somewhere to keep what the machine has done. That is a meter and a stream of readings.
The plan consuming these numbers belongs to our preventive maintenance plan data model, and the level a meter hangs off to the asset hierarchy data model.
Plants that struggle with meters keep them as one column on the asset record, called something like current hours. It can be overwritten, has no history and cannot be audited.
| Object | What it is |
|---|---|
| Meter | A named counter on one asset, with a unit and a rule |
| Reading | One value at one moment, kept forever |
| Source | The device or person the reading came from |
| Trigger | The link from a meter to a plan and its interval |
The reading stream is append only: a wrong reading is corrected by a new row that supersedes it, so the correction stays visible.
A cumulative meter is an odometer, so usage is the difference between two readings. An incremental meter is a tally, so usage is the sum of the rows.
A spot meter is neither: a filter differential of 0.42 bar is a state, so subtracting two of them says nothing about wear.
| Accumulation | Usage rule | Example |
|---|---|---|
| Cumulative | current minus previous | Run hours 41,250 h |
| Incremental | sum of the rows | 7.5 h this shift |
| Spot | no delta, compare to a limit | 0.42 bar across a filter |
Four shift entries of 7.5, 8.0, 7.0 and 8.5 hours are 31.0 hours of work read as incremental.
Read as cumulative they give deltas of plus 0.5, minus 1.0 and plus 1.5, netting to 1.0 hour, so a 500 hour service arrives thirty one times later than it should.
Six families cover almost every counter a plant will put on a plan, and the unit matters as much as the type.
| Meter type | Unit | Drives |
|---|---|---|
| Running hours | h | Bearings, oil, belts, drives |
| Cycles or strokes | count | Presses, dies, moulds, valves |
| Units produced | pcs, kg, m | Web tooling, knives, nozzles |
| Distance | km | Forklifts, AGVs, conveyors |
| Energy or fuel | kWh, L | Compressors, gensets |
| Condition | bar, °C, µm | Filters, hydraulics, bearings |
A press standing idle with a tool mounted burns hours and no tool life, so hours are the wrong meter for wear per operation.
A die hit counter is one cycle meter with a complication of its own: it belongs to the tool, not the press it sits in (see die maintenance tracking).
Units produced carries a trap: a scrapped part wears the tool as much as a good one. Feed the meter total parts, and keep good parts for the OEE data model.
The fields a meter needs before a single reading can be trusted.
| Field | Type | Req. |
|---|---|---|
| meter_ | key | Yes |
| asset_ | ref | Yes |
| meter_ | enum | Yes |
| uom | enum | Yes |
| accumulation | enum | Yes |
| decimals | int | Yes |
| rollover_ | int | No |
| wrap_ | int | No |
| offset | num | No |
| era_ | num | No |
| reset_ | ts | No |
| max_ | num | Yes |
| read_ | int | Yes |
| stale_ | int | Yes |
| primary_ | ref | Yes |
| is_ | bool | Yes |
Six of them carry the weight.
| Field | Why it exists |
|---|---|
| accumulation | Decides whether usage is a subtraction or a sum |
| rollover_ | Highest value the counter shows before it wraps to zero |
| offset | Lifetime total at the moment the current counter started |
| max_ | Fastest credible gain per hour, used to reject bad rows |
| stale_ | Days of silence before the meter is called untrustworthy |
| decimals | 0 for strokes, 1 for hours, so rounding is a decision |
Tenths stored as an integer lose up to 0.9 hours per reading, which at one reading a day is up to 328 hours a year.
stale_after stops silence reading as health: a meter unread past its limit should mark its plans unverified, not comfortably not yet due.
One row per value, written once, never updated. These columns are the minimum for recalculating a due date from scratch a year later.
| Field | Type | Req. |
|---|---|---|
| reading_ | key | Yes |
| meter_ | ref | Yes |
| reading_ | num | Yes |
| reading_ | ts | Yes |
| recorded_ | ts | Yes |
| recorded_ | ref | Yes |
| source_ | ref | Yes |
| entry_ | enum | Yes |
| is_ | bool | Yes |
| estimate_ | enum | No |
| status | enum | Yes |
| supersedes_ | ref | No |
| void_ | text | No |
| usage_ | num | Derived |
| lifetime_ | num | Derived |
| note | text | No |
Two timestamps, not one: reading_at is when the counter showed the value, recorded_at when the row landed. A technician who reads at 06:10 and types at 14:40 moves a usage rate by half a shift.
The last two columns are derived and never typed: when a correction arrives you replay them forward from that point and touch nothing before it.
A reading has three legitimate states and no fourth. Deleting rows is what makes a meter history impossible to audit.
| Status | Meaning |
|---|---|
| Valid | Counts towards usage |
| Superseded | Replaced by a later, corrected row |
| Void | Kept, excluded from usage, reason stored |
Void is for the impossible reading, such as a typo of 412,500 for 41,250, kept with its reason so a changed usage total can be explained.
Counters reach a CMMS two ways, both normal: a machine connection reads the value, or a person types it in.
| Field | What it holds |
|---|---|
| source_ | The key |
| source_ | PLC tag, IoT sensor, vision count, manual, import |
| address | Tag name, register or device id |
| scale_ | Raw units to meter units, for example 0.1 |
| poll_ | How often the value is pulled |
| priority | Which source wins when two disagree |
| last_ | Last time the source answered at all |
| is_ | May write without review |
scale_factor is where quiet disasters live. A PLC publishing tenths of an hour as an integer needs 0.1, and without it every run hours plan fires ten times too late.
priority decides which wins once a machine is connected and both hand entry and the connection write to it (see machine monitoring system).
A naive usage calculation is current minus previous. It is correct until one of three things happens, and then it is not just wrong but confidently wrong.
All three examples use press PRS‑04 and its stroke counter MTR‑1181, a five digit register with rollover_max 99,999.
On 2026-07-01 the counter reads 93,180, and on 2026-07-08 it reads 300.
The naive delta is minus 92,880. Some systems store it, most drop it, a few take the absolute value.
Store rollover_max as the highest value displayed, not the capacity: a five digit counter is 99,999 while the modulus is 100,000.
The formula assumes at most one wrap between readings, since a counter round twice looks like one round once.
So it sets a reading frequency rule. PRS‑04 averages 985 strokes a day, and 100,000 divided by 985 is 101.5 days of headroom.
On 2026-10-05 the press controller fails and is swapped. The old one last showed 87,740 and the new one starts at zero.
On 2026-10-12, after a normal week, the new counter shows 6,895, so the naive delta is minus 80,845.
A system that sees a negative delta and applies the rollover fix returns minus 80,845 plus 100,000 = 19,155 strokes. That is positive, plausible and 12,260 too high.
Nobody notices: the machine appears to work harder than it does, and every cycle based plan comes due early forever.
The fix is an era: a stretch of readings from one physical counter, closed and reopened when the device changes.
| Era field | Value on 2026-10-05 |
|---|---|
| era_ | 2026-10-05 11:20 |
| era_ | 0 |
| offset | 1,987,740 |
| wrap_ | 0, reset with the device |
| reason | Controller replacement |
The offset is the lifetime total reached when the new counter started, here 1,987,740 strokes.
So 6,895 on 2026-10-12 means a lifetime of 1,987,740 plus 6,895 = 1,994,635, and the week's usage is 6,895, not 19,155.
The third case looks like the second and is not: nothing was replaced, and somebody pressed reset on a working counter.
During the clutch service on 2026-10-18 an engineer clears run hours counter MTR‑1180, which read 41,500 h. A week later it shows 70 h, right for a week at ten hours a day and nonsense as a lifetime.
Without a reset_at record the consequence is not a wrong usage number. It is a plan that never speaks again.
| After the reset | Value |
|---|---|
| last_ | 41,500 h |
| next_ | 43,000 h |
| Counter now reads | 70 h |
| Plan waits for | 42,930 h |
| At 10 h a day | 4,293 days, 11.8 years |
The plan is not overdue, not red and not on anybody's list, just not yet due, the most expensive state a preventive plan can be in.
Record reset_at and the value at the reset and the lifetime carries on: 41,500 plus 70 = 41,570 h. Better still, never reset a lifetime counter.
Order matters more than any single line: check the era before the rollover, or case two silently becomes case one.
| Step | Rule |
|---|---|
| 1 | Different era? usage = (era end minus prev) plus (current minus era start) |
| 2 | Same era, current below prev, rollover_ |
| 3 | Same era, current below prev, no rollover_ |
| 4 | Otherwise usage = current minus previous |
| 5 | Reject if usage is above max_ |
lifetime = offset + wrap_count × (rollover_max + 1) + (reading_value minus era_start_value).
On 2026-07-01 that is 0 + 18 × 100,000 + 93,180 = 1,893,180. On 2026-09-23, after the July wrap, it is 0 + 19 × 100,000 + 75,920 = 1,975,920.
Every meter needs a ceiling on credible speed.
| Meter | Ceiling per hour elapsed |
|---|---|
| Run hours | 1.0 h, by physics |
| Strokes | Rated strokes per minute times 60 |
| Units | Ideal cycle rate, no more |
| Distance | Rated speed of the truck |
Between 2026-09-16 and 2026-09-23 there are 168 elapsed hours, and MTR‑1180 gained 70 of them, which is 41.7% utilisation and believable.
A gain of 190 hours over the same 168 is impossible, and belongs in a review queue rather than in four plans.
Readings get missed, and the hole has to be filled, because a due date calculation needs a value for the day it runs. The question is how it is labelled.
One rule, not negotiable: an estimate is written as a reading with is_estimated true and an estimate_method beside it, so it can be replaced the moment a real reading arrives.
A back-fill happens after the next real reading lands, so the gap is bounded and the arithmetic is interpolation.
A forward estimate happens while the gap is open, so it extrapolates from a rolling average and will be wrong.
Take the missed week: no reading on 2026-08-19, with 35,100 on 2026-08-12 and 47,500 on 2026-08-26 either side.
| Method | Value for 08-19 |
|---|---|
| Gap, 14 days | 47,500 minus 35,100 = 12,400 |
| Back-fill, half of it | 35,100 + 6,200 = 41,300 |
| 28 day rate to 08-12 | 27,860 ÷ 28 = 995.0 a day |
| Forward, 7 at 995.0 | 35,100 + 6,965 = 42,065 |
| Forward minus back-fill | 765 high, 12.3% of the week |
Both are defensible and they disagree, which is the argument for the flag. When the real reading lands, the estimated row is superseded and the derived columns replayed from 2026-08-12.
An estimate does not break the total, which telescopes either way. It corrupts the series: the weekly rate, the rolling average and every forecast using it.
One estimate is a rounding error. A run of them is a forecast built entirely on the assumption that nothing changed.
Suppose the reader is absent for four weeks from 2026-09-23 and the system fills the gap at 985 strokes a day, while the press moves onto a slower job and genuinely runs 700 a day.
| On 2026-10-21 | Strokes |
|---|---|
| Lifetime on 2026-09-23 | 1,975,920 |
| Estimated, 28 at 985 | 27,580 |
| Real, 28 at 700 | 19,600 |
| Estimated lifetime | 2,003,500 |
| Real lifetime | 1,995,520 |
| Overstated by | 7,980 |
The cycle plan is due at 2,010,000 strokes, and each forecast continues at the rate it believes in.
| From 2026-10-21 | Crosses 2,010,000 |
|---|---|
| Estimates: 6,500 at 985 | 6.6 days, 2026-10-28 |
| Reality: 14,480 at 700 | 20.7 days, 2026-11-11 |
| Service pulled early | 14 days, 9,800 strokes |
So carry the estimate rate on every forecast: in the quarter below, 1 of 13 readings was estimated, which is 7.7%.
Past a threshold, suppress the date and show the last real reading and its age. A date nobody can defend is worse than no date.
An asset with one meter is the exception. PRS‑04 carries four, and each answers a different question about wear.
| Meter | Type and unit |
|---|---|
| MTR-1180 | Run hours, h, cumulative |
| MTR-1181 | Strokes, count, cumulative |
| MTR-1182 | Units made, pcs, incremental |
| MTR-1183 | Filter drop, bar, spot |
So meter_id must be a first class key. The stroke counter belongs to the press and the filter differential to the hydraulic unit under it, and a meter on the wrong level is invisible to its plans.
A plan on a real machine needs more than one trigger: hours for time driven wear, cycles for operation driven wear, a date for the asset that barely runs.
So the trigger is not a field on the plan but a child table, and the plan carries a combine_rule of first or all.
| Trigger field | What it holds |
|---|---|
| trigger_ | The key |
| plan_ | Plan it belongs to |
| meter_ | Empty for a calendar trigger |
| interval_ | 1,500 |
| interval_ | run hours |
| last_ | 40,000 h, written on completion |
| next_ | 41,500 h, derived |
| lead_ | 10 |
| avg_ | 84 days |
| combine_ | First |
Each trigger produces a forecast date from its own meter and window. The plan's due date is the minimum of them under first and the maximum under all.
The lead time converts per trigger, and this is where meter plans fall over: ten days means nothing to a counter until you multiply it by a rate.
| 10 day lead | Raise point |
|---|---|
| Calendar, already days | 10 days before due |
| Hours: 10 × 10.0 = 100 h | 41,400 h |
| Cycles: 10 × 985 = 9,850 | 2,000,150 |
Store avg_window on the trigger, because a 28 day window and an 84 day window give different rates and different dates.
When two triggers fall due within days of each other the team should do the work once, which needs a combine_window on the plan.
With a 14 day window, an hours trigger due 2026-10-18 and a cycles trigger due 2026-10-28 are one job, on the earlier date.
Then comes the rule almost every implementation gets wrong. Completing a plan writes back every trigger on it, not only the one that fired.
| Written on 2026-10-18 | Next due |
|---|---|
| Date, 180 days | 2027-04-16 |
| Hours 41,500, plus 1,500 | 43,000 h |
| Strokes 2,000,545, plus 160,000 | 2,160,545 |
Write the real reading at completion into each trigger, never the due value: writing 41,500 when the counter said 41,562 hides the drift.
Skip the write-back on the cycles trigger and it still sits at 2,010,000, which the press crosses ten days later, so the team redoes a service it just finished.
Press PRS‑04, quarter three of 2026, read every Wednesday. Every figure below reconciles with the ones above it.
The counter is MTR‑1181, cumulative, rollover_max 99,999. Thirteen readings, twelve intervals, 84 days from 2026-07-01 to 2026-09-23.
| Date | Counter | Used |
|---|---|---|
| 07-01 | 93,180 | start |
| 07-08 | 300 | 7,120 |
| 07-15 | 7,240 | 6,940 |
| 07-22 | 14,550 | 7,310 |
| 07-29 | 21,230 | 6,680 |
| 08-05 | 28,280 | 7,050 |
| 08-12 | 35,100 | 6,820 |
| 08-19 | 41,300 | est. |
| 08-26 | 47,500 | 12,400 |
| 09-02 | 54,680 | 7,180 |
| 09-09 | 61,630 | 6,950 |
| 09-16 | 68,870 | 7,240 |
| 09-23 | 75,920 | 7,050 |
Two rows are not ordinary. The 2026-07-08 row is lower than the one before because the counter wrapped, and the 2026-08-19 row is a back-filled estimate flagged is_estimated.
The 12,400 on 2026-08-26 covers 14 days, not 7, so it must never enter a weekly average as one week.
Add the usage figures, treating the estimated row as part of the 14 day block it sits in.
Now check it from the lifetime values instead of the deltas, which is the check that catches a missed wrap.
| Lifetime | Strokes |
|---|---|
| 07-01: 18 wraps + 93,180 | 1,893,180 |
| 09-23: 19 wraps + 75,920 | 1,975,920 |
| Difference | 82,740 |
Two independent routes to 82,740, which is the point of the exercise.
| Window to 09-23 | Strokes a day |
|---|---|
| 84 days, 82,740 | 985.0 |
| 28 days, 28,420 | 1,015.0 |
Thirty strokes a day is a 3.0% difference, small because this press ran steadily. Where the product mix changes the two windows can differ by a third.
The run hours meter over the same 84 days went from 40,410 h to 41,250 h, which is 840 hours, or 10.0 hours a day.
Plan PM‑4120 is the slide and clutch service on PRS‑04, last completed on 2026-05-21 at 40,000 h and 1,850,000 strokes.
| Trigger | Due point | Left |
|---|---|---|
| Calendar, 180 d | 2026-11-17 | 55 d |
| Hours, 1,500 h | 41,500 h | 250 h |
| Cycles, 160,000 | 2,010,000 | 34,080 |
Stand on 2026-09-23, at 41,250 h and 1,975,920 strokes, and convert each gap into a date at the quarter's rates.
| Arithmetic | Date |
|---|---|
| 2026-05-21 plus 180 days | 2026-11-17 |
| 250 ÷ 10.0 a day = 25.0 d | 2026-10-18 |
| 34,080 ÷ 985 a day = 34.6 d | 2026-10-28 |
Three triggers on one plan, one asset, one day, and three answers spread across 30 days.
Swap the cycle forecast onto the 28 day rate of 1,015 a day and it becomes 33.6 days, or 2026-10-27, one day of movement from a choice of window.
Under combine_rule first the plan is due on 2026-10-18, the hours trigger, the earliest of the three.
With a 10 day lead the job appears on 2026-10-08, or at 41,400 h, whichever comes first.
The cycles trigger at 2026-10-28 falls inside the 14 day combine_window, so it is folded into the same job.
The calendar date of 2026-11-17 is not wrong, just last, and its job is to catch the quarter when the press barely runs.
So print the trigger, the meter, the reading, the rate and the window on the work order. All three dates are true, and a due date with no trigger name is the one to never act on.
The rest of the plan sits in the CMMS data model, the failures it prevents in the equipment failure record data model.
Ask for a demonstration, not an answer.
Before a shortlist, read what is a CMMS and equipment maintenance software, then the preventive maintenance schedule and condition based maintenance.
Fabrico is an OEE platform with a full CMMS built in. Everything above is a specification to test a shortlist against, not one product's feature list.
On the machine side Fabrico has PLC connectivity, IoT sensors and computer vision, with real-time OEE, downtime and micro-stop detection built on them.
On the maintenance side: recurring templates, conditional tasks, an annual PM plan view, approvals and work shifts, plus push, in-app and email notifications.
Work orders are raised and confirmed by your team, never by the system alone. Technicians use the iOS, Android or web app and scan the QR code on a machine or part.
Inventory holds min and max levels and books consumption against the work order (see spare parts management).
Failure forecasting is not a standard feature, and production planning is not part of it. Put the rollover rule, the era fields and the estimate flag to every vendor you shortlist.
Want your machine data, plans and work orders in one place? Book a 30 minute demo with a Fabrico consultant, no commitment, or contact us with your questions.
reading_id, meter_id, reading_value, reading_at, recorded_at, recorded_by, source_id, entry_method, is_estimated, estimate_method, status and note. usage_delta and lifetime_value are derived from the stream and should never be typed by hand.
A cumulative meter is an odometer, so usage is the current reading minus the previous one. An incremental meter records the amount used since the last entry, so usage is the sum of the rows, and confusing the two can understate usage by a factor of thirty.
Store rollover_max as 99,999 and compute usage as current minus previous plus 100,000, modulo 100,000, so a move from 93,180 to 300 is 7,120 strokes rather than minus 92,880. Read the counter often enough that it can never wrap twice between readings.
Yes, but only with is_estimated set to true and the method recorded, so the row can be superseded when a real reading arrives. Twenty eight days of estimates at 985 strokes a day against a real 700 moved the worked example's due date fourteen days early.
Each trigger stores a meter, an interval and the reading at the last completion, so next due is last done plus the interval, with the lead time converted into meter units by a rolling daily average. With combine_rule set to first, the plan is due on the earliest of its triggers, which in the worked example was 2026-10-18 and not the calendar date of 2026-11-17.
Zakažite sastanak KSNUMKS-to-KSNUMKS sa našim stručnjacima ili se direktno upišite u naš besplatni plan.
Nije potrebna kreditna kartica!