Fuuz Accelerators
Accelerator · Open

A plant, a historian and a Fuuz-shaped API on your laptop.

The ACE Docker Simulator runs a live OPC UA plant, records it into a MongoDB time-series historian, and serves it through a GraphQL API that speaks the same dialect as the Fuuz platform — so what you build against it ports to a real tenant unchanged.

$git clone https://github.com/Fuuz-Platform/ace-docker-simulator && cd ace-docker-simulator && docker compose up -d
http://localhost:8081 No configuration. No account. No tenant required.
88 live tags OPC UA MongoDB time-series GraphQL 5 containers

What runs

Five containers start by default. Two more are opt-in, because they need something only you can supply.

ace-sim :4840 · :4841

An OPC UA server any client can browse and subscribe to. Eight units × eleven signals — process, discrete, counter, enum and string — with rotating fault states (COMMS, STALE, FLATLINE, DRIFT) that emit real OPC UA status codes rather than merely looking wrong.

ace-mongo :27017

MongoDB with the mongot search binary bundled, which is what provides $vectorSearch. Plain community server does not ship it.

ace-graphql :8098

A Fuuz-shaped API over Mongo — edges/node, where:{f:{_eq}}, upsert<Model>(payload:[…]) — plus the historian query surface.

ace-bridge :4842

The fidelity writer. Real OPC UA subscriptions with deadband, and a stored sample on every quality edge.

ace-ui :8081

The operations console. React behind nginx, which reverse-proxies every service under /api/* — same-origin by construction, so there is no CORS anywhere.

ace-loadgen profile: load

The volume writer. Straight to Mongo, because a per-sample GraphQL hop bottlenecks long before the database does — which is also how real collectors work.

ace-orchestrator profile: fuuz

ACE itself: deterministic matching and signal classification against a live Fuuz tenant. Needs a host, a tenant and a token, so it stays off by default.

The console

Plant state, historian throughput and fault distribution, thirty seconds after the clone.

The ACE operations console: simulator up with 88 tags, historian up with 3,028 samples, vector index ready, and a bar chart of plant units by fault state.

A historian, not a table of numbers

Most "historian" demos store a timestamp and a float. That model quietly lies about a real plant in five specific ways, and each one is fixed here.

CapabilityWhy it is not optional
Quality on every sampleBad samples are stored, not dropped — discarding them hides outages and makes coverage a lie. Aggregates exclude them and report badSamples separately.
Two timestampsSource time and receive time. The difference is late arrival: gateway store-and-forward replays hours-old samples out of order after a WAN outage, and latencyMs makes that visible.
Typed valuesRunning=1 and Speed=1.0 are not the same kind of fact. Process is float, discrete is bool, enum is int, batch context is string.
Interpolation per tagContinuous tags interpolate; discrete tags are step functions. Linear interpolation of a state signal invents states that never happened.
Time-weighted aggregatesException-based samples are irregular, so an arithmetic mean over-weights whatever happened to be sampled often. Continuous tags get a time-weighted mean; discrete tags get state durations and transition counts.
The difference is not academic. On the same window of the same tag, arithmetic mean reads 121.332 where the time-weighted mean reads 121.589 — and for a discrete tag the honest answer is not a mean at all, it is ON 143s / OFF 37s, 79.7% on-fraction, 6 transitions.

Measured, not estimated

Numbers from a sustained run on an Apple M5 Max. Reproduce them with docker compose --profile load up -d.

24,415/s
samples sustained across 20,000 tags, zero errors
6.1 bytes
per sample on disk — 2.1× compression
14.45M
samples stored in the measured run
99.18%
Good quality; the rest split across Bad, Stale and OutOfRange

Quick start

Docker is the only prerequisite.

  1. Clone and start

    Five containers, no configuration file, nothing to sign up for.

    # the plant, the historian, the API and the console
    git clone https://github.com/Fuuz-Platform/ace-docker-simulator
    cd ace-docker-simulator
    docker compose up -d
  2. Open the console

    localhost:8081 — the plant is already running and the bridge is already recording it.

  3. Ask the historian something

    The behavioural statistics that drive signal classification are computed in the database, so the client never streams raw history.

    curl localhost:8098/graphql -H 'content-type: application/json' -d '{
      "query": "{ historianStatus { name status count } }"
    }'
  4. Optional — point ACE at a live Fuuz tenant

    Copy .env.example to .env, fill in your host, tenant and token, then bring up the fuuz profile.

    cp .env.example .env
    docker compose --profile fuuz up -d