Digital Factory

Industrial connectors for Unreal Engine 5.8

Plant data,
as Blueprint tags.

Five connectors that read live values from the equipment you already run — PLCs, brokers and OPC UA servers — and publish them into Unreal as named tags you can bind to. No middleware, no bridge service, no polling loop to write.

DIGITAL FACTORY TAG REGISTRY 0 updates
TagValueQualityUpdated
5Protocols, sold separately
0Plugin dependencies
43Automated tests per build
C++Full source included

In every connector

The parts nobody enjoys writing twice

Tag registry

A Game Instance Subsystem holding every value the connector has seen. Bind On Tag Updated, or read a tag by name. Connectors are kept alive by the registry, so one created in a Blueprint survives without being stored in a variable.

Reconnect that behaves

Fixed or exponential backoff with a delay ceiling, an optional attempt limit, and jitter — so a hundred clients pointed at one PLC don't retry in lockstep as it reboots. An outage reports twice: when it starts and when it gives up.

Credentials by reference

A config stores where a secret lives — an environment variable name or a file path — never the secret. Configs get exported, diffed and committed; a password inside one leaks the first time that happens.

Deduplication

Suppress broadcasts when a value hasn't changed, with a numeric deadband to absorb sensor noise. The deadband compares against the last broadcast value, so a reading creeping just under the threshold still reports eventually.

Writing, not just reading

Every connector sends as well as receives — Modbus writes registers and coils, OPC UA writes nodes, MQTT and AMQP publish, REST issues a request on demand. Writes are queued from the game thread and executed on the worker, so a Blueprint never blocks on a network round trip, and the call reports whether it was accepted rather than pretending it was delivered.

Runtime log level

Set verbosity from Blueprint using the familiar debug/info/warn/error/critical names. Works in packaged builds, where there is no console to type into.

Readable source

Full C++ ships with every connector, commented to explain why rather than what. Automated tests come with it — run them yourself from the Session Frontend.

Connector

Modbus TCP

Polls coils, discrete inputs, holding registers and input registers from a Modbus TCP device using a register map you define, and publishes each entry as a tag. Writes single coils and single registers back.

Function codesRead 0x01 0x02 0x03 0x04, write 0x05 0x06
FramingMBAP over Unreal's own sockets
Data typesUInt16, Int16, UInt32, Int32, Float32, with configurable word order
Read strategyAdjacent registers coalesced into as few requests as the device allows
WritesQueued from the game thread, executed on the worker, echo verified
AuthenticationNone — the protocol carries no identity
Third-party codeNone redistributed
// One entry in the register map
Address       2        // zero-based protocol address
Type          Float32
Word Order    HighFirst
Tag Name      oven.temp_c
Function      ReadHoldingRegisters
Watch the addressing. This connector uses zero-based protocol addresses. The 4xxxx display convention starts at 40001 — there is no 40000 — so holding register 40001 is address 0, and the conversion is subtract 40001 rather than 40000. Vendors are inconsistent about this, and some document the raw address already. If every tag reads like its neighbour, the base is the first thing to check.

Getting started

  1. Enable the plugin. Install it from Fab, then in the editor open Edit → Plugins, search for Digital Factory Modbus, enable it and restart.
  2. Start the simulator. In a console, from the plugin's Content/Python/ folder, run python modbus_sim.py. It listens on 127.0.0.1:502 and needs nothing but Python 3. For a Fab install, the plugin folder is under your engine's Engine/Plugins/Marketplace/.
  3. Open the Level Blueprint and, from Event BeginPlay, add Get Digital Factory Modbus Data Subsystem. That is the tag registry: every value the connector reads lands there.
  4. Bind the two events before connecting, so neither the first value nor a failure goes unseen. Bind Event to On Tag Updated with a custom event that breaks the tag and prints its Raw Tag Name and the To Display String (Digital Factory Value) of its Value. Then Bind Event to On Connector Error and print the message — every diagnostic arrives there.
  5. Create and connect. Add Make Digital Factory Modbus Config, add one entry to Registers and give it a Tag Name — every other default already matches the simulator's register 0. Feed it to Create Modbus Connector, then call Connect and print its return value: false means the config was unusable, and the log says why.
  6. Press Play. counter prints once a second, climbing by one — the same number the simulator's console shows it serving.
// Level Blueprint, in execution order
Event BeginPlay
→ Get Digital Factory Modbus Data Subsystem
→ Bind Event to On Tag Updated
→ Bind Event to On Connector Error
→ Create Modbus Connector  ← Make … Modbus Config
→ Connect                  → Print String

// the On Tag Updated event
Break Digital Factory Modbus Tag Value
→ Raw Tag Name, To Display String (Value)
→ Print String
// Make Digital Factory Modbus Config
Host            127.0.0.1             default
Port            502                   default
Registers[0]
  Tag Name      counter
  Function      ReadHoldingRegisters  default
  Address       0                     default
  Data Type     UInt16                default

Testing without hardware

Content/Python/modbus_sim.py ships with the plugin: a Python server that serves changing values and prints what it served — decoded back out of the response bytes rather than recomputed, so the console and Unreal genuinely agree rather than merely appearing to. Standard library only, so there is nothing to install.

It answers reads and writes both. A written value is remembered from then on, so the register stops moving and reads back what you wrote: a simulator that accepted a write and carried on generating would be indistinguishable from one that discarded it.

Public simulators such as diagslave also work, but their register tables start at zero and stay there — which looks exactly like a broken decode.

python modbus_sim.py

# listening on
127.0.0.1:502

# holding / input registers
0      UInt16    counter, +1/s
1      Int16     sine, -1000..1000
2-3    Float32   20.0..25.0, high word first
4-5    UInt32    +10/s, high word first

# coils / discrete inputs
0      toggles once per second
1      always on
2      always off

Screenshots

Captured against Content/Python/modbus_sim.py, which ships with this connector — so every frame below is reproducible on your own machine with nothing else installed.

What this connector doesn't do

  • Win64 only. That is what the descriptor declares, and the only platform built or tested.
  • Discrete inputs and input registers are read-only. That is the protocol, not the plugin. Writes cover single coils (0x05) and single holding registers (0x06).
  • Modbus TCP carries no identity. There is no authentication and no encryption in the protocol to expose, so anyone who can reach the port can read and write. Put it on a segment you trust.
  • Tag quality stays Unknown. Modbus has no quality field, and reporting Good would be inventing data. Nothing sets Stale yet either, so a device that stops answering leaves its last values sitting there looking current.
  • Values are stored as double. No practical limit at 16 and 32 bits — noted because it is the same storage every connector in the range uses.

Support

Questions, bug reports and licence queries go to info@edelmeier.com. Name the connector — Digital Factory Modbus TCP — and your engine version. The five are sold, versioned and updated separately, so which one you have changes the answer.

Connector

REST

Polls a JSON-over-HTTP endpoint on an interval and flattens the response into tags. For the MES, historian or line-controller that already exposes an API.

TransportUnreal's own HTTP stack
FlatteningNested objects to dotted names, arrays to bracketed indices
Root pathScope the walk to a nested object with a dotted path
NullsPublished as an empty value, never dropped — so a source that stops reporting is distinguishable from one that hasn't changed
SendingSend Request — one GET, POST or PUT on demand, outside the polling loop, reusing the configured endpoint and credentials
AuthenticationBasic, Bearer token, or API key in a named header
Third-party codeNone redistributed
// Response
{ "line1": { "sensors": [ { "temp": 20.4 } ],
            "vibration": null } }

// Tags produced
line1.sensors[0].temp   20.4
line1.vibration         (empty, quality Unknown)
A null is a reading, not an absence. Dropping it would leave the previous value in the registry, so an endpoint that stopped reporting would look identical to one whose value simply had not changed. The quality stays Unknown rather than Bad: plenty of systems use null to mean something specific — a drive at standstill, a probe not fitted — and calling that a fault would be our inference, not the source's statement.

Getting started

  1. Enable the plugin. Install it from Fab, then in the editor open Edit → Plugins, search for Digital Factory REST, enable it and restart.
  2. Start the simulator. In a console, from the plugin's Content/Python/ folder, run python rest_sim.py. It serves on http://127.0.0.1:18080 and needs nothing but Python 3. For a Fab install, the plugin folder is under your engine's Engine/Plugins/Marketplace/.
  3. Open the Level Blueprint and, from Event BeginPlay, add Get Digital Factory RESTData Subsystem. That is the tag registry: every value the connector reads lands there.
  4. Bind the two events before connecting, so neither the first value nor a failure goes unseen. Bind Event to On Tag Updated with a custom event that breaks the tag and prints its Raw Tag Name and the To Display String (Digital Factory Value) of its Value. Then Bind Event to On Connector Error and print the message — every diagnostic arrives there.
  5. Create and connect. Add Make Digital Factory RESTConfig and set Url to http://127.0.0.1:18080/api/telemetry — GET, a one-second poll and no credentials are all defaults. Feed it to Create RESTConnector, then call Connect and print its return value: false means the config was unusable, and the log says why.
  6. Press Play. Tags such as plant.line1.sensors[0].temp and plant.line1.oven.setpoint_c print on every poll. Wait a minute and plant.line2.vibration_mm_s goes empty for eight seconds while its neighbours carry on — that is a reported null, working.
// Level Blueprint, in execution order
Event BeginPlay
→ Get Digital Factory RESTData Subsystem
→ Bind Event to On Tag Updated
→ Bind Event to On Connector Error
→ Create RESTConnector  ← Make … RESTConfig
→ Connect                → Print String

// the On Tag Updated event
Break Digital Factory RESTTag Value
→ Raw Tag Name, To Display String (Value)
→ Print String
// Make Digital Factory RESTConfig
Url                     http://127.0.0.1:18080/api/telemetry
Verb                    GET    default
Poll Interval Seconds   1.0    default
Credentials
  Scheme                None   default

Testing without a server of your own

Content/Python/rest_sim.py ships with the plugin: a JSON endpoint serving changing plant telemetry. Standard library only, so there is nothing to install on a machine you would rather not install anything on.

It is built around the awkward cases rather than the tidy ones — nested objects and an array for dotted and indexed names, every value type in one payload, a nested path worth aiming Response Root Path at, and optional bearer-token auth that refuses anything else with a 401 naming the header it wanted.

One sensor reports null for eight seconds each minute while the rest of the document keeps going, so you can watch a single tag go empty beside its neighbours. That case is invisible in a connector that drops nulls, which is exactly why it is worth being able to produce on demand.

Any other JSON endpoint works too. Prefer this one for anything you intend to keep: a public API rate-limits, changes its schema without telling you, and eventually stops existing.

# no auth
python rest_sim.py

# require Authorization: Bearer s3cret
python rest_sim.py --token s3cret

# listening on
http://127.0.0.1:18080

# endpoints
/                 the whole document
/api/telemetry    the same, for a config
                  that wants a path
/api/setpoint     POST or PUT {"value": 214.5};
                  comes back in the next poll as
                  plant.line1.oven.setpoint_c
/health           {"ok": true}, no auth

# a scoped walk: set Response Root
# Path to plant.line1 and the prefix
# disappears from every tag name

Screenshots

Captured against Content/Python/rest_sim.py, which ships with this connector — so every frame below is reproducible on your own machine with nothing else installed.

What this connector doesn't do

  • Win64 only. That is what the descriptor declares, and the only platform built or tested.
  • Polling only. No websockets, no server-sent events. The connector re-reads on the interval you set, so the shortest delay you can have is that interval.
  • Response Root Path scopes to an object, not a value. It narrows the walk to a sub-document; it cannot pick out a single leaf.
  • Tag quality stays Unknown. HTTP carries no quality field. Nothing sets Stale yet, so an endpoint that stops changing and one that stops responding look alike.
  • Values are stored as double. JSON integers above 253 lose precision — worth knowing if a payload carries identifiers or counters that large.

Support

Questions, bug reports and licence queries go to info@edelmeier.com. Name the connector — Digital Factory REST — and your engine version. The five are sold, versioned and updated separately, so which one you have changes the answer.

Connector

MQTT

Subscribes to broker topics and publishes arriving messages as tags, deriving tag names from the topic hierarchy. Decodes Sparkplug B payloads where your edge nodes publish them.

ClientEclipse Paho MQTT C, under the Eclipse Distribution License 1.0
Payload formatsRaw, JSON, and Sparkplug B
Sparkplug BMetric aliases resolved against birth messages; sequence gaps reported; alias table dropped when a node reports death
Tag namingGroup, node, device and metric — so two devices publishing the same metric name don't collide
PublishingPublish and Publish Bytes — topic, payload, QoS 0–2, optional retain
AuthenticationUsername and password
EncryptionNone in this release
// Subscribe to the whole namespace, not just data
Topic Filter     spBv1.0/#
Payload Format   SparkplugB

// DDATA carries an alias, never a name.
// Names arrive only in birth messages, so
// subscribing to data alone leaves every
// value permanently unidentifiable.
Sparkplug decoding is verified against an independent encoder. The decoder's own tests would pass even if a field number had been misread from the specification, because fixture and decoder would share the mistake. A separate Python encoder, checked byte-for-byte against Eclipse Tahu's schema, produces the payloads used to test it.

Getting started

  1. Enable the plugin. Install it from Fab, then in the editor open Edit → Plugins, search for Digital Factory MQTT, enable it and restart.
  2. Start a broker and publish one message. Any MQTT broker works; Eclipse Mosquitto is the easiest, and on Windows its installer registers it as a service, so it may already be running. Publish with the retain flag, -r, as shown opposite: MQTT is push-only, so nothing arrives until something publishes, and a retained message is replayed to the connector the moment it subscribes.
  3. Open the Level Blueprint and, from Event BeginPlay, add Get Digital Factory MQTTData Subsystem. That is the tag registry: every value the connector receives lands there.
  4. Bind the two events before connecting, so neither the first value nor a failure goes unseen. Bind Event to On Tag Updated with a custom event that breaks the tag and prints its Raw Tag Name and the To Display String (Digital Factory Value) of its Value. Then Bind Event to On Connector Error and print the message — every diagnostic arrives there.
  5. Create and connect. Add Make Digital Factory MQTTConfig, add one entry to Topics and set its Topic Filter to dfp/test/# — the broker address tcp://127.0.0.1:1883 and the Json payload format are defaults. Feed it to Create MQTTConnector, then call Connect and print its return value: false means the config was unusable — an empty Topic Filter is the usual cause — and the log says why.
  6. Press Play. dfp.test.line1.temp prints 21.5. Publish to dfp/test/line2 and a second set of tags appears with no change to the config — that is the wildcard fanning out.

Then Sparkplug B. Add a second entry to Topics with Topic Filter spBv1.0/# and Payload Format SparkplugB, and from the plugin's Content/Python/ folder run python sparkplug_sim.py. It publishes real birth and data messages through mosquitto_pub, so that has to be on your PATH. Expect PlantA.Edge1.Press01.Pressure, .Cycles and .Running — names resolved from aliases the data messages never spell out. For a Fab install, the plugin folder is under your engine's Engine/Plugins/Marketplace/.

# retained, so it replays when the connector subscribes
mosquitto_pub -r -t dfp/test/line1 -m "{\"temp\":21.5}"

# arrives as
dfp.test.line1.temp    21.5
// Level Blueprint, in execution order
Event BeginPlay
→ Get Digital Factory MQTTData Subsystem
→ Bind Event to On Tag Updated
→ Bind Event to On Connector Error
→ Create MQTTConnector  ← Make … MQTTConfig
→ Connect                → Print String

// the On Tag Updated event
Break Digital Factory MQTTTag Value
→ Raw Tag Name, To Display String (Value)
→ Print String
// Make Digital Factory MQTTConfig
Server Uri        tcp://127.0.0.1:1883  default
Topics[0]
  Topic Filter    dfp/test/#
  Payload Format  Json                  default
Topics[1]                               for Sparkplug B
  Topic Filter    spBv1.0/#
  Payload Format  SparkplugB

Screenshots

Captured against Eclipse Mosquitto, a third-party broker, with Sparkplug payloads produced by Content/Python/sparkplug_sim.py, which ships with this connector. Mosquitto is not included and is not required — any MQTT broker will do.

What this connector doesn't do

  • Win64 only. That is what the descriptor declares, and the only platform built or tested.
  • No TLS. ssl:// fails outright: no TLS backend is compiled, so credentials and payloads cross the wire in the clear. Fine against a broker on the same machine, unsuitable for a plant network.
  • Sparkplug B is decode-only. Reading works; issuing NCMD or DCMD does not, because that needs a protobuf encoder and this release ships only a decoder. Raw MQTT publishing is available on the same connector — it is Sparkplug's command semantics specifically that are absent.
  • With Clean Session on, QoS 1 and 2 protect a live connection only. Queuing across a disconnect needs Clean Session off and a fixed Client Id; a generated one gets a new queue every time.
  • Tag quality stays Unknown. MQTT carries no quality field. Nothing sets Stale yet, so a topic that goes quiet keeps its last value.
  • Values are stored as double. Sparkplug long metrics above 253 lose precision.

Support

Questions, bug reports and licence queries go to info@edelmeier.com. Name the connector — Digital Factory MQTT — and your engine version. The five are sold, versioned and updated separately, so which one you have changes the answer.

Connector

AMQP

Consumes a queue on a RabbitMQ broker and publishes each message as tags, taking the tag name from the message's routing key.

Protocol versionAMQP 0-9-1
Libraryrabbitmq-c, under the MIT licence
ModelConsumes an existing queue; declares no queues or exchanges of its own
Payload formatsRaw and JSON
PublishingPublish — exchange, routing key, body, optional persistence
AcknowledgementAutomatic, or manual with a prefetch limit when losing a message matters
AuthenticationUsername and password
EncryptionNone in this release — TLS sources are excluded from the build entirely
This is AMQP 0-9-1, not AMQP 1.0. They share a name and very little else. If your broker is Azure Service Bus, Solace or ActiveMQ speaking 1.0, this connector will not talk to it. If it is RabbitMQ, it will.

Getting started

  1. Enable the plugin. Install it from Fab, then in the editor open Edit → Plugins, search for Digital Factory AMQP, enable it and restart.
  2. Start RabbitMQ and give it a queue. RabbitMQ is not included; Docker is the quickest way to run it, as shown opposite. Then, from the plugin's Content/Python/ folder, run python amqp_setup.py: it creates the queue dfp.tags, binds it for line1.# and publishes test messages into it. They wait in the queue, so it does not matter that the connector starts later. The connector itself never declares queues — in a plant, that belongs to whoever runs the broker. For a Fab install, the plugin folder is under your engine's Engine/Plugins/Marketplace/.
  3. Open the Level Blueprint and, from Event BeginPlay, add Get Digital Factory AMQPData Subsystem. That is the tag registry: every value the connector consumes lands there.
  4. Bind the two events before connecting, so neither the first value nor a failure goes unseen. Bind Event to On Tag Updated with a custom event that breaks the tag and prints its Raw Tag Name and the To Display String (Digital Factory Value) of its Value. Then Bind Event to On Connector Error and print the message — every diagnostic arrives there.
  5. Create and connect. Add Make Digital Factory AMQPConfig, set Queue Name to dfp.tags, and under Credentials choose Username Password with user dfp and the secret shown opposite. Host, port, virtual host and the Json payload format are defaults. Feed it to Create AMQPConnector, then call Connect and print its return value: false means the config was unusable, and the log says why.
  6. Press Play. line1.machine2.temp, line1.machine2.ok and line1.machine3.temp print — the routing key and the JSON keys, joined — and the queue depth in RabbitMQ's management page at http://localhost:15672 drops to zero.
Not guest/guest. RabbitMQ refuses its guest account from anywhere but localhost, and a container reached through a published port arrives from Docker's own network — so guest fails exactly like a wrong password. That is why the container is started with a dfp user. Inline Development Only is for this first run; for anything real, set Source to Environment Variable and put the variable's name in Value.
# one command
docker run -d --name dfp-rabbit -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=dfp -e RABBITMQ_DEFAULT_PASS=dfp rabbitmq:3-management

# then, from Content/Python
python amqp_setup.py
// Level Blueprint, in execution order
Event BeginPlay
→ Get Digital Factory AMQPData Subsystem
→ Bind Event to On Tag Updated
→ Bind Event to On Connector Error
→ Create AMQPConnector  ← Make … AMQPConfig
→ Connect                → Print String

// the On Tag Updated event
Break Digital Factory AMQPTag Value
→ Raw Tag Name, To Display String (Value)
→ Print String
// Make Digital Factory AMQPConfig
Host              127.0.0.1                default
Port              5672                     default
Virtual Host      /                        default
Queue Name        dfp.tags
Payload Format    Json                     default
Credentials
  Scheme          Username Password
  Username        dfp
  Secret
    Source        Inline Development Only
    Value         dfp

Screenshots

Captured against RabbitMQ, a third-party broker, with the queue and messages set up by Content/Python/amqp_setup.py, which ships with this connector. RabbitMQ is not included and is not required — though it is the broker this connector targets, since AMQP 0-9-1 is what it speaks.

What this connector doesn't do

  • Win64 only. That is what the descriptor declares, and the only platform built or tested.
  • This is AMQP 0-9-1, not AMQP 1.0. It speaks to RabbitMQ. It cannot talk to Azure Service Bus, Solace or IBM MQ, which are 1.0 — a different protocol that happens to share a name.
  • No TLS. Credentials cross the wire in the clear via SASL PLAIN. Same caveat as MQTT: local broker fine, plant network no.
  • Publishing returns “queued”, not “delivered”. Publisher confirms are not enabled, so a successful return means the broker accepted the frame, not that anything consumed it.
  • Tag quality stays Unknown. AMQP carries no quality field. Nothing sets Stale yet, so a queue that stops delivering keeps its last values.
  • Values are stored as double. Integers above 253 in a JSON body lose precision.

Support

Questions, bug reports and licence queries go to info@edelmeier.com. Name the connector — Digital Factory AMQP — and your engine version. The five are sold, versioned and updated separately, so which one you have changes the answer.

Connector

OPC UA

Connects to OPC UA servers and publishes node values as tags. Subscribes by default, with polling available for servers that limit subscription counts. Implements an OPC UA client using the open62541 stack.

Stackopen62541 v1.5.6, under MPL-2.0
Data deliverySubscriptions with configurable publishing interval, sampling interval and queue size; polling as a fallback
Security modesNone, Sign, and Sign & Encrypt
Security policiesBasic256Sha256, Aes128Sha256RsaOaep, Aes256Sha256RsaPss
WritingWrite Bool, Write Number, Write String — the numeric type is stated by the caller, because the server checks it
QualityMapped from the server's status code by severity, including the informational Good_ sub-codes
IdentityAnonymous, or username and password; client certificate with trust list and optional revocation lists
CertificatesGenerate Self Signed Certificate in Blueprint — OPC UA authenticates both ends, so a client needs one of its own
Crypto libraryThe OpenSSL that ships with Unreal Engine; nothing new is redistributed
Value conversionScalars only; arrays and structs are skipped with a warning
// Use the endpoint URL your server advertises,
// verbatim -- including the path. Ports vary by
// product and version; do not assume a default.
Endpoint  opc.tcp://HOST:PORT/PATH
Security  SignAndEncrypt
Policy    Basic256Sha256
Node Id   ns=3;i=1001
Use the host name, not localhost. An OPC UA client connects to whatever the server advertises in its endpoint list, which is its own host name. Pointing at localhost can connect and then stall on the redirect.
Deprecated policies are not offered. Basic128Rsa15 and Basic256 are absent by choice — the stack is built without them. Password-protected private keys are also unsupported, since the password would have to live somewhere the config could reach it, which defeats protecting the key.

Getting started

  1. Enable the plugin. Install it from Fab, then in the editor open Edit → Plugins, search for Digital Factory OPC UA, enable it and restart.
  2. Start a server. Any OPC UA server works; the free Prosys OPC UA Simulation Server is the easiest, and is not included. Recent versions ship with unencrypted endpoints switched off, so in its Expert Mode enable SecurityPolicy None for this first connection. Note the endpoint it reports — typically opc.tcp://your-host-name:53530/OPCUA/SimulationServer.
  3. Open the Level Blueprint and, from Event BeginPlay, add Get Digital Factory OPCUAData Subsystem. That is the tag registry: every value the connector reads lands there.
  4. Bind the two events before connecting, so neither the first value nor a failure goes unseen. Bind Event to On Tag Updated with a custom event that breaks the tag and prints its Raw Tag Name and the To Display String (Digital Factory Value) of its Value. Then Bind Event to On Connector Error and print the message — every diagnostic arrives there.
  5. Create and connect. Add Make Digital Factory OPCUAConfig and paste the server's endpoint into Endpoint Url, host name and all — not localhost. Add one entry to Nodes: namespace 3, identifier type Numeric, identifier 1001, which is Prosys' counter. Security policy None and anonymous credentials are defaults. Feed it to Create OPCUAConnector, then call Connect and print its return value: false means the config was unusable, and the log says why.
  6. Press Play. counter prints each time the server's value changes, with quality Good — the one connector whose quality comes from the source rather than staying Unknown.
Policy None encrypts nothing. It is the shortest route to a first value, not a way to run. Sign & Encrypt needs a client certificate and a trust exchange with the server — generate it with Generate Self Signed Certificate, let the first connection be rejected, then trust it on the server. The user guide that ships with the plugin walks through it under Getting connected the first time, and the screenshots below show it against this same server.
// Level Blueprint, in execution order
Event BeginPlay
→ Get Digital Factory OPCUAData Subsystem
→ Bind Event to On Tag Updated
→ Bind Event to On Connector Error
→ Create OPCUAConnector  ← Make … OPCUAConfig
→ Connect                 → Print String

// the On Tag Updated event
Break Digital Factory OPCUATag Value
→ Raw Tag Name, To Display String (Value)
→ Print String
// Make Digital Factory OPCUAConfig
Endpoint Url       opc.tcp://HOST:53530/OPCUA/SimulationServer
Security
  Policy           None      default
Credentials
  Scheme           None      default
Nodes[0]
  Tag Name         counter
  Namespace Index  3         default is 2
  Identifier Type  Numeric   default is String
  Identifier       1001

Screenshots

Captured against the Prosys OPC UA Simulation Server, a third-party product of Prosys OPC Ltd. It is not included with this connector and is not required to use it — any OPC UA server will do. It appears here because the screenshots show a real session with a real server rather than a mock-up, and the certificate trust dialog in particular is that server's own.

What this connector doesn't do

  • Win64 only. That is what the descriptor declares, and the only platform built or tested.
  • Scalars only. Arrays and structures are skipped with a warning rather than half-converted.
  • Values are stored as double. Int64 above 253 loses precision.
  • Quality is real here, but Stale is not. This is the one connector that reports quality properly, mapped from the server's status code. What it still does not do is notice absence: nothing sets Stale, so a node that stops arriving keeps its last value and its last quality.

Support

Questions, bug reports and licence queries go to info@edelmeier.com. Name the connector — Digital Factory OPC UA — and your engine version. The five are sold, versioned and updated separately, so which one you have changes the answer.

Licensing

What you're redistributing

Every connector ships full C++ source and a licence inventory naming each third-party component, its version, its upstream commit and its terms. Nothing here requires your project to be licensed under anything.

ConnectorThird-party componentLicence
Modbus TCPNone—
RESTNone—
AMQPrabbitmq-cMIT
MQTTEclipse Paho MQTT CEclipse Distribution License 1.0 (BSD-3-Clause)
OPC UAopen62541MPL-2.0 — file-level, does not extend to your code

No GPL or LGPL anywhere

Audited specifically, because copyleft of that strength would prevent redistribution inside a proprietary project at all. Two licence documents in the tree contain the words "GNU General Public License" — both inside a definition of the term "Secondary License", not as a dependency.

MPL-2.0 is file-level

Its obligations attach to the open62541 files themselves and do not extend to code that merely links against them — the decisive difference from GPL. The sources ship unmodified, with SHA-256 hashes published so you can verify that.

Built with AI assistance

A material portion of the C++ was written with a large language model, and the Fab listings are tagged accordingly. Every connector has been verified against real servers and brokers, not only against its own tests.