Skip to content

Scene Graph

The scene graph is a C# object graph that models the physical plant — equipment, pipes, structures, and instruments. It is rendered server-side to SVG and streamed to the browser via SignalR. Equipment symbols follow the ISA 5.1 standard.

How it works

sequenceDiagram
    participant PLC as PLC
    participant Orin as Edge Device
    participant Grain as DeviceInstanceGrain
    participant SG as Scene Graph
    participant SR as SceneRenderer
    participant Hub as SignalR Hub
    participant Browser as Browser

    PLC->>Orin: Tag value change
    Orin->>Grain: gRPC tag update
    Grain->>SG: TagBinding resolves
    SG->>SG: Mark dirty nodes
    SG->>SR: Re-render dirty SVG fragments
    SR->>Hub: Push SVG patch
    Hub->>Browser: WebSocket message
    Browser->>Browser: Patch DOM

Target latency: PLC tag change → visual update in the browser in under 500ms.

Type hierarchy

Core nodes

Node Purpose
Scene Root container — one per process visualisation
SceneNode Base class for all nodes in the tree
GroupNode Logical grouping of child nodes
ViewportNode Defines a pannable/zoomable view area
LayerNode Z-ordered rendering layer (e.g. pipes behind equipment)

Primitives

Node Purpose
Rect Rectangle with optional corner radius
Circle Circle or ellipse
Path SVG path for arbitrary shapes
Text Text label with font, size, and alignment

Equipment (ISA 5.1)

Node Purpose
PumpNode Centrifugal, positive displacement, submersible
MotorNode Electric motor with rotation indicator
ValveNode Gate, globe, butterfly, check — with position binding
TankNode Vessel with level indicator
PipeNode Pipe segment with flow animation
InstrumentNode Pressure, temperature, flow, level transmitter
ConveyorNode Belt conveyor with speed and running state
CrusherNode Jaw, cone, impact crusher
ScreenNode Vibrating screen with throughput indicator
FeederNode Apron, belt, vibratory feeder

Structures

Node Purpose
DamNode Tailings dam or water storage with level
BuildingNode Structure outline — crusher house, substation

Annotations

Node Purpose
TagDisplayNode Live tag value with label and engineering units
AlarmIndicatorNode Alarm state indicator (flashing, colour-coded)
HealthBadgeNode Equipment health summary from edge ML

Animation

Type Purpose
Flow Animated dashes along a pipe to indicate flow direction and rate
Pulse Pulsing glow on active equipment
Rotation Spinning motor or pump impeller
Level Fill level animation in tanks and dams

Data binding

Scene graph nodes are bound to live data through binding objects:

Binding Source Example
TagBinding Device instance tag value Pump speed → motor rotation rate
StateBinding Discrete state (running, stopped, faulted) Valve state → open/closed symbol
AlarmBinding Alarm state (active, acknowledged, cleared) High temp → red indicator flash
AIBinding Edge ML inference result Anomaly score → health badge colour

When a bound value changes, the binding marks its node (and any ancestors that need re-compositing) as dirty. The renderer only re-renders dirty subtrees.

State flow — PLC to pixel

The full data path from a PLC register change to a visual update:

  1. PLC — a tag value changes (e.g. pump speed ramps up).
  2. Edge device — the Orin reads the new value via libplctag (EtherNet/IP).
  3. gRPC — the edge device sends the tag update to the site server.
  4. DeviceInstanceGrain — the grain stores the new value and publishes to the tag stream.
  5. TagBinding — the binding on the scene graph node resolves the new value.
  6. Dirty marking — the node and its rendering ancestors are marked dirty.
  7. SceneRenderer — re-renders only the dirty SVG fragments.
  8. SignalR — pushes the SVG patch to connected browsers.
  9. Browser — the TypeScript client patches the DOM node in place.

Rendering evolution

The scene graph is designed as an abstract model that is independent of the rendering backend:

Phase Renderer Status
Current Server-side SVG Production
Next 2D raster canvas (server-side) Planned
Future 3D WebGL / AR Research

Because rendering happens on the server and only the output (SVG, canvas frames, or 3D scene descriptions) is sent to the client, the browser client never changes when the rendering backend evolves. The same TypeScript shell that patches SVG today will receive canvas frames or WebGL scene updates tomorrow.