Docs/Infohub (Flow)/T10: A KPI Dashboard
Infohub (Flow)How-to

T10: A KPI Dashboard

Layer KPIs from base measures upward, build period comparison, and design a dashboard someone would actually use.

Updated 15 Oct 2026Infohub Docs

What you will build: a layered KPI set with period comparison, presented as a dashboard designed for one audience.

Time: about 40 minutes.

You need: T01–T09 completed.


Step 1 — Think in layers#

A KPI is rarely one calculation. It is a small stack, and building it deliberately makes it debuggable.

Layer 4   Composite KPI          OEE, cost per unit
Layer 3   Ratio KPIs             availability, yield, specific energy
Layer 2   Interval quantities    bottles/day, kWh/day, downtime min/day
Layer 1   Base measures          tag values, event durations, manual entries

The rule that saves hours: each layer should be inspectable. When a Layer 4 number looks wrong, you can look at Layer 3, then Layer 2, and find the step that failed. A single heroic expression combining eight inputs gives you nothing to look at but its output.

Step 2 — Complete Layer 2#

You have most of it. Add what is missing so every input exists at Daily:

Measure Interval Type
Bottles Daily Daily Aggregated, Sum
Rejects Daily Daily Aggregated, Sum — from a new Counter chain on FL001.BottleCount.Reject
Energy Daily Daily Aggregated, Sum
Water Used Daily Daily Aggregated, Sum
Downtime Minutes Daily Daily Event-aggregated, Active Duration
Production Target Daily Daily Calculated from the monthly manual target

Add the rejects chain the same way as bottles: retrieved Tag measure, Counter to Hourly, Sum to Daily.

Step 3 — Build Layer 3#

Four ratio KPIs, all guarded.

Availability:

Period.Duration.TotalMinutes == 0 ? 0
    : (Period.Duration.TotalMinutes - (Measure_<downtimeDailyId>.Value ?? 0))
      / Period.Duration.TotalMinutes * 100

Quality rate — good output as a share of total:

((Measure_<bottlesDailyId>.Value ?? 0) + (Measure_<rejectsDailyId>.Value ?? 0)) == 0 ? (double?)null
    : Measure_<bottlesDailyId>.Value
      / ((Measure_<bottlesDailyId>.Value ?? 0) + (Measure_<rejectsDailyId>.Value ?? 0)) * 100

Specific energy:

(Measure_<bottlesDailyId>.Value ?? 0) == 0 ? (double?)null
    : Measure_<energyDailyId>.Value / Measure_<bottlesDailyId>.Value * 1000

Attainment against target:

(Measure_<targetDailyId>.Value ?? 0) == 0 ? (double?)null
    : Measure_<bottlesDailyId>.Value / Measure_<targetDailyId>.Value * 100

Note the deliberate use of null rather than zero on the three ratios. All of these will be averaged into monthly figures, and a shutdown day contributing 0% would drag those averages below reality. Availability returns 0 because zero availability on a day with no production is arguably true — but you could equally argue for null there too, and the point is that it is a decision, not a default.

Set units and formats: % with 0.0 '%' for the three percentages, kWh/1000btl with 0.00 for specific energy.

Step 4 — Add period comparison#

The question people always ask next is "compared with what?"

Expressions can reach back through periods. Add:

Yesterday's value, for a same-period comparison:

Measure_<attainmentId>[-1].Value

Change against yesterday, as a percentage:

(Measure_<attainmentId>[-1].Value ?? 0) == 0 ? (double?)null
    : (Measure_<attainmentId>.Value - Measure_<attainmentId>[-1].Value)
      / Measure_<attainmentId>[-1].Value * 100

Format it with an explicit sign so direction is unambiguous: +0.0;-0.0;0.0.

A rolling seven-day average, to smooth the noise:

Measure_<attainmentId>[-7, 0].Values.Where(v => v.HasValue).Average()
Tip

A rolling window is one measure, not seven. The window form [-7, 0] returns a collection with a .Values array, and LINQ works on it. Building a rolling seven-day average as seven separate measures plus a sum is a common early mistake — it triples the model and breaks the day somebody asks for fourteen days instead.

Step 5 — Add month-to-date#

A to-date measure accumulates as the period progresses. Add monthly aggregations:

Measure Interval Aggregation Source
Bottles MTD Monthly Sum Bottles Daily
Energy MTD Monthly Sum Energy Daily
Downtime Minutes MTD Monthly Sum Downtime Minutes Daily

Then recalculate the monthly ratios from the monthly quantities, not by averaging the daily ratios:

(Measure_<bottlesMtdId>.Value ?? 0) == 0 ? (double?)null
    : Measure_<energyMtdId>.Value / Measure_<bottlesMtdId>.Value * 1000
Important

Do not average a ratio to get a period ratio. A monthly specific energy is total energy over total production for the month — not the average of thirty daily ratios. Averaging weights every day equally regardless of how much ran, and produces a number that will not reconcile with the monthly totals. Somebody will check, and they will be right.

Step 6 — Design the dashboard for one audience#

Decide who this is for before placing anything. Take a plant manager at a desk, reviewing yesterday.

That fixes the choices: daily interval, month-to-date context, trends over a month, moderate detail, read at arm's length.

+----------------------------------------------------+
| Filler 1 — Yesterday and Month to Date             |  Text, 12 × 1
+-----------+-----------+-----------+----------------+
| Attainment| Availabil.| Quality   | kWh/1000       |  Widgets, 3 × 3
+-----------+-----------+-----------+----------------+
| Bullets: the four KPIs against target              |  6 × 3
+----------------------------------------------------+
| Trend: production vs target, 30 days               |  12 × 5
+-------------------------+--------------------------+
| Trend: specific energy   | Table: last 7 days      |  6 × 4 each
+-------------------------+--------------------------+

Status, then target comparison, then trend, then detail. It matches the order the questions arrive in.

Step 7 — Make the widgets carry status#

Bind limits onto every widget so they colour themselves. For attainment, point the limit at Production Target Daily so it tracks the target rather than a constant — the pattern from T08.

A widget with a number and no colour makes the reader do the judging. A widget with a limit-driven colour does the judging for them, which is the entire point.

Step 8 — Label the periods#

The widgets show yesterday (or today so far, depending on how you configured them). The trends show 30 days. The MTD measures show the month.

Say so. A Text panel heading stating "Yesterday, with month-to-date context" prevents the single most common dashboard misreading — comparing a partial period against a complete one.

Step 9 — Apply the removal test#

Look at every panel and ask: if this were gone, what decision would get worse?

Anything with no answer should go. Dashboards accumulate panels and almost never lose them, so this needs doing deliberately — and it is the highest-value fifteen minutes in dashboard design.

You will probably find that one of the four widgets is not actually used, and that the seven-day table duplicates the trend.

What you have learned#

Build KPIs in layers. Each layer inspectable, so a wrong number can be traced.

Guard every division, and choose null or zero deliberately — a ratio that gets averaged should return null on a non-production period.

Reach other periods with [-1] and [-7, 0]. Period comparison and rolling averages are expressions, not extra measures.

Recalculate period ratios from period totals. Never average a ratio.

Design for one audience. Interval, density, and element size all follow from who is reading and from where.

Bind limits so widgets carry status.

Label the period on every dashboard.

Remove what nobody acts on.

Try this before moving on#

Build the same KPI set as a monthly dashboard for a management review — five elements, monthly interval, year-to-date context, comparison against the same month last year (Measure_<id>[-12].Value).

You will find you cannot simply reuse the daily dashboard. The interval is different, so every chart is different, and the density that suits a desk review is wrong for a meeting. That is the lesson: audience determines the dashboard, and one dashboard cannot serve two.


Next: T11: Custom Functions — stop repeating yourself.

esc
↑↓ navigate · ↵ open · ⌘K anywhere