Year
2026
Status
In production
Mattec Live Production Dashboard
A production-floor application built on an undocumented MES data layer that kept injection molding operations visible through an extended vendor-system outage.
- Node.js
- Express
- JavaScript
- SQL Server
- Mattec ProHelp MES
- Reverse engineering
- Manufacturing systems
- SQL Server
- Operational applications
- Production-safe writes
- Data validation
- Legacy systems
The plant’s vendor MES front end became unavailable during an extended systems outage. Production did not stop, so the need did not either: supervisors still had to see which presses were running, which were down and why, and what each job was doing against its standard.
I built an internal application directly against the MES data layer. It became the production-floor view for the injection molding department and has held that role for weeks rather than days.
What it does
- Live machine status across the floor, with per-state counts in the header
- Filtering by state, so a screen can be left showing only exceptions
- Per-job production information and job detail
- Job cycle history, over a configurable window — a few hundred cycles for a quick read, a few thousand when a trend matters
- Process-parameter history, including cycle time and shot weight
- Linked charts across parameters, so a crosshair on one lines up on all of them
- A table view of the same numbers, because operators frequently want the values, not the shape
- Scrap detail, and corrections to recorded scrap
- Downtime detail, and reassignment of a recorded downtime reason to the correct one
- Live downtime reason where the underlying system exposes one
Polling and caching
Every display on the floor shows the same board, so the naive design has each browser querying the MES database on its own timer. That multiplies one query by however many screens are mounted, against a database that is also running production.
Instead the browser polls frequently and the server holds a short-lived cache in front of the database. A representative configuration used a browser refresh around twenty seconds against a server cache around ten, so the board stays current while the database sees roughly one query per cycle no matter how many screens are watching. The exact intervals are configuration; the point is that read amplification is handled once, on the server, rather than being a property of how many displays someone mounts.
What made this difficult
This was not a dashboard on top of a clean API. The MES is a legacy manufacturing system with little usable documentation, and its stored values have to be interpreted empirically before anything can be built on them. Nearly every one of the findings below would have produced a plausible, confidently wrong dashboard if taken at face value.
Speed is time per part, not throughput
Standard and actual speed are seconds per part. A higher actual value therefore means a slower machine.
Read as throughput — the way the field name invites — every performance colour on the board inverts: the presses running behind standard turn green and the healthy ones turn red. This single unit question decided whether the board was useful or actively misleading.
Actual speed is also a job-lifetime average, not a current reading. A machine that ran badly early in a long job keeps a poor average after it recovers, and one that is degrading right now can still look fine. So the off-standard state honestly means this job has been slow on average, not this machine is slow at the moment. Current behavior lives in the cycle history instead, which reads cycle by cycle.
The downtime code and its description disagree
The system’s downtime code and downtime description do not always agree. The description commonly carries a placeholder meaning “downtime happened and nobody assigned a reason”, and that placeholder persists on machines that are plainly producing.
Colouring on the description alone therefore marks almost the whole board as down, and the board stops meaning anything. Application logic cannot assume the most human-readable field is the authoritative one; which field is trustworthy has to be established per field.
Mold and job relationships change the grain
Two-sided tooling produces parent and child job structures. Filtering by the parent returns both halves, and summing that double-counts.
The fix is not a clever query, it is being explicit about grain: know which key is the real job key for each table, and never aggregate across a pair that shares recorded values. Getting this wrong produces numbers that look reasonable and are exactly twice the truth.
Scrap is stored at one grain and derived at three
Scrap has a genuine grain — a quantity per machine, shift, job and defect code — and above it sit several derived rollups, by job, by code and by shift.
Nothing in the database maintains those rollups. There is no trigger and no procedure that keeps them in step, which means correcting scrap is not a single update: the write has to rebuild every derived level itself, inside one transaction, or the detail and the totals silently disagree.
Corrections are also necessarily per shift. A job spanning many shifts has no derivable answer to “which shift does this correction belong to”, so the interface asks rather than guessing.
Downtime corrections go through the vendor’s own procedure
Live machine state belongs to the data collector, and cannot be set from outside it. I tested several mechanisms for writing live state and none of them held — which turned out to be the wrong goal anyway.
The distinction that mattered: you cannot set what a machine is doing now, but you can correct what was recorded — and correcting the record is what supervisors actually want.
Recorded downtime is adjusted through the MES’s existing pAdjustDown stored procedure, which moves
time and occurrences from one downtime code to another within a job and shift. Two things justified
this over direct table writes. First, it validates its inputs and raises rather than corrupting.
Second, the system’s own audit table shows years of plant personnel using it — so this is an
existing production workflow, not a workaround invented from outside.
The write path is testable the same way the scrap path is: run it inside a transaction against live data, verify the effect, roll back, and confirm the database is unchanged.
Defensive field resolution
Some source views expose columns whose names contain spaces and whose values arrive padded to a fixed width. Hard-coding those names couples the application to a view definition it does not own.
Instead each logical field resolves against a list of candidate names, values are trimmed and coerced on the way through, and an introspection command reports any field that no longer resolves. If the view changes shape, the failure is a named report rather than a column of blanks.
The state model
Four states, evaluated in order. Precedence matters because more than one condition is usually true at once.
- OfflineNo job loaded on the machine.
- DowntimeA real downtime reason is assigned, or the machine reports no cycle time at all.
- Off standardCycle time exceeds the standard by more than the configured tolerance.
- RunningEverything else.
Engineering principles this demonstrates
Reverse engineering before implementation. Guardrails before writes — the write paths are disabled by default, verified under rollback, and routed through supported entry points. Explicitness about grain and units. And documentation as part of the deliverable: each finding above is recorded with the evidence that settled it, so the next person to change this does not have to rediscover it.