Skip to content

Twin Models

Define industrial asset types in .twin.yaml files with typed members, parameters, generators, methods, lifecycle stages, fault modes, and runtime bindings.

A model (.twin.yaml) defines an industrial asset type. Here is the structure.

schema_version: "2"
model:
name: my-sensor
description: "Simple temperature sensor"
members:
temperature:
kind: variable
data_type: Double
access: read
unit: degC
generator:
type: noise
center: 22.0
stddev: 0.5
schema_version: "2"
model:
name: <identifier> # Lowercase, numbers, hyphens. 1-64 chars.
namespace: <dotted-string> # Optional. e.g. com.acme.pumps
description: <string>
parameters:
<name>:
type: float | int
default: <number>
min: <number> # Optional
max: <number> # Optional
unit: <engineering-unit> # Optional
methods:
<MethodName>:
description: <string>
input_arguments: # Optional
<arg_name>:
data_type: Double | Float | Int32 | UInt16 | UInt32 | Boolean | String
unit: <string> # Optional
description: <string>
runtime: # Optional -- makes the method executable
type: set-members | set-parameters | clear-faults
writes: # For set-members and set-parameters
- member: <name>
value: <literal> # Or argument: <arg_name>
output_arguments:
<arg_name>:
data_type: <type>
description: <string>
members:
<name>:
kind: variable | property
data_type: Double | Float | Int32 | UInt16 | UInt32 | Boolean | String
access: read | readwrite | write
unit: <engineering-unit> # Optional
eu_range: [<min>, <max>] # Optional
default: <value> # Optional
enum_values: [...] # Optional, for String members
generator: { ... } # Optional, drives value each tick
binding: { ... } # Optional, binds to runtime state
lifecycle:
stages:
- name: <string>
duration_hours: [<min>, <max>]
effects:
<member_name>: { multiplier: <n>, offset: <n> }
faults:
- name: <string>
trigger:
lifecycle_stage: <stage> # Optional
condition: "<expr>" # Optional, e.g. "pressure < 2.5"
probability: <0-1>
effects:
<member_name>:
spike: <n> # Optional, multiplicative
multiplier: <n> # Optional
offset: <n> # Optional
duration_ticks: <n>

Parameters are tunable constants that vary per instance. Reference them in generators with $param.name:

parameters:
rated_rpm:
type: float
default: 1750
min: 500
max: 3600
unit: rpm
members:
speed_feedback:
kind: variable
data_type: Double
access: read
unit: rpm
generator:
type: noise
center: "$param.rated_rpm"
stddev: 5

Templates can override parameters per instance. The variation setting applies random spread around parameter defaults.

Members are processed in declaration order. This matters for generators that reference other members — the referenced member must be declared earlier.

Special members can bind to runtime state instead of using generators:

fault_active:
kind: variable
data_type: Boolean
access: read
binding:
type: fault-active # true when any fault is active
lifecycle_stage:
kind: variable
data_type: String
access: read
binding:
type: lifecycle-stage # Current lifecycle stage name

Available binding types: fault-active, fault-count, active-fault-code, lifecycle-stage, health-state, availability-state.

Methods define executable actions. When a method has a runtime block, it can be invoked over any enabled protocol (MQTT, OPC UA, and future adapters):

methods:
SetSpeed:
description: "Set the pump speed reference."
input_arguments:
speed_rpm:
data_type: Double
unit: rpm
description: "Requested shaft speed."
runtime:
type: set-members
writes:
- member: speed_command
argument: speed_rpm
output_arguments:
accepted:
data_type: Boolean
description: "True when accepted."

Runtime types: set-members (write member values), set-parameters (write parameters), clear-faults (reset all active faults).

Members and parameters use a controlled vocabulary of engineering units (e.g., rpm, bar, degC, kW, m3/h). These units propagate to all protocol outputs — MQTT payloads, OPC UA EUInformation nodes, and any future protocol adapters.