Case 02 · OPC line telemetry · ERP · conforming layer
From the plant floor to the P&L
Production data arrives continuously, per machine, on the plant's clock. Financial data arrives in periods, per product, on the ledger's clock. Putting them on one chart naively does not merge two truths — it manufactures a third one that is wrong.
Role
Design and build
Scale
Continuous line data
Systems
OPC, Dynamics AX / D365
Team
Built solo
01 — The situation
What was actually happening
Operations had line telemetry: events per machine, effectively per second, describing what the equipment was doing. Finance had the ledger: postings per product, per accounting period, describing what the business had earned.
Both groups wanted the same view — produced volume against financial margin — and both had already tried to build it. Each attempt produced a chart where the two series disagreed by an amount nobody could explain, so each attempt was abandoned, and the two functions carried on running separate versions of the same month.
A chart where two trusted series disagree by an unexplained margin does not cost you one number. It costs you both of them.
02 — The constraint
Why the obvious fix does not work
The disagreements were not bugs. They were the honest consequence of two systems measuring different things and calling them the same word.
A unit on the line is a thing the machine completed. A unit in the ledger is a thing that was sold, or transferred, or written down — and a unit produced on the last night shift of the month may post in either period depending on when it cleared inspection. A plant day runs shift to shift and does not align to a calendar day, which does not align to an accounting day in a 4-4-5 calendar.
None of that is resolvable by a better join. It is resolvable only by deciding, explicitly and in writing, what each term means and what happens at every boundary where the two systems cannot be made to agree.
03 — The approach
What I built
I designed a conforming layer that sits between the two sources and does three things before any reporting touches the data: it applies an explicit definition of a unit per system, it maps plant days to accounting days through a rule rather than a timestamp comparison, and it carries a declared tolerance for the residue.
Rows that fall outside tolerance are not dropped and not silently adjusted. They are routed to a quarantine that is itself reported, with the reason attached. The exception queue became one of the more useful outputs of the project — it surfaced a recurring inspection-timing issue that neither function had been able to see from its own side.
Telemetry and ledger meet only after the conforming layer has applied a unit definition, a calendar map and a declared tolerance. What cannot be reconciled goes to a reported quarantine, never into the chart.04 — Judgement
Decisions, and what each one cost
Every one of these had a defensible alternative. The trade-off is stated because the trade-off is the point.
Decision 01
Conform in the layer, not the dashboard
Every previous attempt had reconciled inside the visualisation, which meant the reconciliation was invisible, unversioned, and different in each chart. Moving it into a modelled layer made it one artefact that could be reviewed and tested.
Cost: slower to change than editing a chart expression, which was unpopular at first.
Decision 02
Write the unit definition down
The single highest-value hour of the project was the one spent getting operations and finance to state, in one sentence each, what they counted as a unit. They had never been in the same room for that question.
Cost: it surfaced a disagreement that had to be escalated rather than modelled around.
Decision 03
Calendar mapping as a rule
Mapping a plant day to an accounting day by comparing timestamps works until a shift crosses midnight at a period boundary. Making it an explicit, inspectable rule meant the edge case was handled once and identically everywhere.
Cost: the rule needs maintaining when the plant changes shift patterns.
Decision 04
Quarantine, do not drop
Silently discarding rows that will not reconcile produces a clean chart and an undetectable error. Reporting them produced an uglier first view and a much faster path to the underlying process problem.
Cost: an exception queue is only useful if somebody works it, which required naming an owner.
05 — Outcome
What changed
Volume and margin on one reconciling view, with the residue visible rather than hidden.
Operations and finance stopped maintaining separate months and started disputing the same figure.
The exception queue found a real process defect in inspection timing that neither function could see alone.
The reconciliation became reviewable — one modelled artefact rather than logic scattered across charts.
OPC telemetry
Dynamics AX / D365
Python
Grain reconciliation
Calendar mapping
Data quality
06 — Retrospective
What I would do differently
Honestly assessed, with hindsight
I built the conforming layer before I had a written owner for the exception queue, and for the first few weeks the quarantine simply accumulated. A reported exception nobody is accountable for is just a slower way of dropping the row. I would now treat naming that owner as a precondition for shipping, not a follow-up.
I would also push harder, earlier, on the unit-definition disagreement rather than modelling around it while it was escalated. Carrying both definitions through the layer for a period added complexity that a decision two weeks sooner would have removed entirely.