2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30
2026-08-24 15:35:32 +05:30

LINE-1 Digital Twin — customer demo

A live digital twin of a five-station production line: 3D view, streaming telemetry, OEE, trend-based failure prediction, injectable faults, what-if controls, and an LLM copilot that diagnoses the line from its own telemetry.

Built to be presented from a laptop, offline if necessary.

npm install
npm run dev

Dashboard on http://localhost:5173, API on http://localhost:8787. For a single-port build with no dev server: npm run serve, then http://localhost:8787. Full setup detail is in docs/installation.md.

The line starts pre-warmed — 30 simulated minutes of history, a full OEE window at a realistic ~88%, and anomaly baselines already learned. You are never presenting a dashboard that just booted.

Read this first

  • docs/installation.md — requirements, install, how to run it, the demo sequence, switching models, and troubleshooting.
  • docs/LINE-1-Digital-Twin-Demo.pptx — the step-by-step demonstration deck. 16 slides, one per beat, with the presenter actions and script in the speaker notes. Every chart is measured data.
  • docs/demo-script.md — the same 6-minute run of show in text form: what to click, what to say, and the questions you will get.
  • docs/architecture.md — where the customer's MQTT, OPC-UA or historian data plugs in.

The demo in one paragraph

The line runs at a believable 88% OEE. You change the oven setpoint and watch a thermal model respond with a real time constant — that is what makes it a twin rather than a dashboard. You inject a bearing fault, run the clock at 60×, and the twin projects the alarm threshold before it is crossed. The copilot — which is never told what you injected — traces vibration to tool wear to reject rate to OEE. It drafts the work order. You replace the bearing and watch OEE recover. Then you switch the copilot to its offline rule engine and get the same diagnosis with no model and no network, which answers "is the AI making this up?" better than any claim could.

What is honest about this

Worth knowing, because it is what makes the demo survive an engineer in the room.

  • OEE is ~88%, not 100%. Micro-stops and unplanned stops are modelled, so the number moves for reasons you can name. Performance is capped at 100% by definition, so draining a WIP buffer can never produce an OEE above 100%.
  • Prediction is trend extrapolation, not AI. A least-squares fit over recent run time, projected to the threshold, suppressed when r² is too low to trust. Call it what it is; the AI story is the copilot.
  • The copilot is not told which fault was injected. Operator injection log lines are filtered out of its context. The diagnosis is earned from telemetry.
  • Stations block and starve each other through finite WIP buffers. Stop the packer and the backup propagates upstream until the whole line is blocked.
  • A dropped sensor holds its last value and says so. Charts stop rather than drawing a flat line that would read as a healthy steady state.
  • The copilot always answers. A dead key, an unreachable host, or a model that returns an empty response all degrade to the rule engine.

Copilot providers and the settings page

Click AI ⚙ in the header to open the settings page. It lists the models each provider actually offers — pulled live from the OpenRouter catalogue (400+, with context length and price per million) and from your local Ollama library — and lets you switch provider, model, temperature, output budget and reasoning effort without restarting anything. The dashboard keeps streaming behind it.

Mode First token Notes
Cloud — OpenRouter 2–35 s, highly variable Best analysis. Default stealth/ox-alpha.
Local — Ollama ~1–2 s Run ollama serve. Best for the live beat.
Offline rules instant No network, no model. Deterministic.

Measure before you present. Each model panel has a Test button that reports time-to-first-token and grades it: under 4 s is presentable live, 4–12 s means a noticeable pause, above that is follow-up-only. Cloud reasoning-model latency was measured between 2 s and 36 s on consecutive identical calls, so this is worth checking on the day rather than assuming.

Changes persist to .copilot-settings.json (gitignored), so a model chosen while preparing survives a restart. Reset to defaults discards it and falls back to .env. Settings never contain the API key — that stays in the environment.

Resolution order when provider is Auto: OpenRouter if a key is set, else Ollama if it answers, else the rule engine. The copilot panel's own selector still overrides per question.

Copy .env.example to .env for the initial defaults. Notes:

  • OLLAMA_HOST is often set machine-wide to 0.0.0.0 by Ollama itself. That is a bind address and not dialable — the server normalizes it to 127.0.0.1.
  • Thinking models (the Qwen3 family) are sent think: false. Without it they spend the whole token budget reasoning and return empty content.
  • COPILOT_PROVIDER=fallback forces the rule engine, for rehearsing the worst case.
  • Anything set in the settings page takes precedence over .env until you reset it.

Verification

Two suites, no browser needed. Run both after any change to the model.

node scripts/simcheck.mjs

Checks the model tells the truth: signals stay in physical range, faults move OEE the right way in a controlled A/B against an unfaulted line, packer stoppage propagates upstream as blocking, a dropped sensor goes stale rather than to zero, setpoint changes are lagged, runs are reproducible from a seed, and the prediction arrives before the threshold is crossed (~435 s of simulated lead time).

node scripts/apicheck.mjs   # server must be running

Checks the WebSocket stream, the control API, fault propagation into the stream, and that the copilot answers in every provider mode with answers grounded in real readings.

Layout

server/
  sim/         line.js (part flow, WIP, blocking) · stations.js (physics)
               faults.js (injectable profiles) · kpi.js (OEE)
  analytics/   trend.js (least squares) · anomaly.js (frozen baselines)
               alarms.js (thresholds, latching, predictions)
  ingest/      source.js (the seam) · simulatedSource.js
               mqttSource.js, opcuaSource.js (documented stubs)
  ai/          copilot.js (providers) · context.js (prompt) · rules.js (offline)
               settings.js (validated, persisted runtime config)
web/src/
  three/       Scene.jsx, Station.jsx, Conveyor.jsx
  panels/      KPIs, station strip, trend charts, alarms, what-if, copilot
               SettingsPage.jsx (provider/model picker + latency test)
scripts/       dev.mjs, simcheck.mjs, apicheck.mjs

The 3D scene loads no external assets — no HDRI, no CDN font, no texture. It is primitives and analytic lights, so it works in a room with no wifi.

Notable design decisions

  • A server owns the simulation, not the browser. It keeps the API key server- side, gives the ingest layer a real seam, and makes "swap in your historian" true rather than aspirational.
  • Signals declare their analytics eligibility. cumulative and volatile flags in stations.js exclude a signal from anomaly testing — normal tool wear accumulation is not an anomaly.
  • Anomalies only alarm in the direction that is bad, inferred from which thresholds a signal declares. A tool change dropping wear from 18% to 2% must not raise an alarm for being better than baseline.
  • Separate RNG streams for signal noise, stall decisions and reject rolls. Sharing one stream put the reject roll at a correlated phase in the sequence and measurably biased it — a 2.0% reject rate was firing at 5.7%.
  • Single dark theme, deliberately. This is an ops dashboard on a projector. Chart colours come from a validated palette; status colours are reserved and always paired with a text label, never carrying meaning by hue alone.
S
Description
No description provided
Readme
233 KiB
Languages
JavaScript 92.4%
CSS 7.5%
HTML 0.1%