Ecosystem Architecture
How the undoRT components fit together, and where the project stands today.
The Integration Pipeline
The full undoRT pipeline moves from Structured Text source to deterministic hardware execution in four stages. The first three — the undoStudio IDE the source is written in, the st2cpp compiler and the undoCore foundation library — are now released. The remaining components are in active development.
st2cpp — Semantic Analysis & a JSON Data Layer
Before generating any code st2cpp runs a two-pass semantic analyzer — symbol table, IEC case-insensitive resolution, type checking, interface and inheritance verification, circular-dependency detection — in Strict (fail-fast) or Permissive (incremental adoption) modes, so errors are caught at build time rather than on the target.
Around the compiler sits a bidirectional JSON data layer: libraries and project
data are declared in versioned JSON schemas (Library Descriptor v1.0, Project Configuration v1.0),
imported through --ext-libs, resolved by the semantic analyzer, and bound by the code
generator. The same pipeline runs in reverse: analyzed ST is exported back to a JSON library
descriptor (semantic-only, round-trippable) — from the CLI via --export-descriptor or
programmatically via LibraryDescriptorBuilder.
A third JSON document completes the picture. Task Configuration v1.0
(tasks.json, --tasks) declares the cyclic tasks of a PLC: which
programs each one runs, at which cycle, priority and CPU core. The compiler turns it into a
Runtime.cpp with one undoPLC Master per PLC instance and one Worker per task,
so the transpiled code is a runnable PLC rather than a set of translation units. Each
PROGRAM is instantiated once per calling task, as in TwinCAT.
- •
Semantic Analysis— 2 passes, type checking,--strict/--permissive, process-imageATvalidation - •
External Libraries— JSON descriptors, registry, codegen bindings - •
JSON Export— ST → semantic-only descriptor → JSON, guaranteed round trip - •
Task Configuration— Master/Worker runtime emitted around the generated code - •
765 tests— run automatically on every commit
Shared Core: undoCore
undoCore is the header-only foundation library that provides common contracts and data structures across all undoRT projects. It defines:
- •
IoBus— Abstract interface for fieldbus master synchronization - •
ProcessImage— Double-buffered memory layout for IEC 61131-3 (%I, %Q, %M) - •
STArray— IEC 61131-3 compatible array with arbitrary bounds - •
VAR_IN_OUT— Reference wrapper for in-out parameters - •
IEC 61131-3 Types— Complete set of elementary types mapped to C++17
The Deterministic Execution Loop
Traditional SoftPLCs introduce overhead through intermediate virtual machines or interpretation layers. undoPLC bypasses this by executing native C++ binaries compiled directly from Structured Text by st2cpp — no VM, no interpreter.
When paired with undoOS (a Linux Preempt-RT kernel configuration), real-time tasks are pinned to isolated CPU cores using FIFO scheduling priorities. Fieldbus communication via undoBUS (IgH EtherCAT Master) runs deterministically in user-space alongside the PLC runtime, using the IoBus interface defined in undoCore.
The generated C++ compiles and runs standalone on any C++17-capable platform, so the
compiler is useful without a real-time target. Passing --tasks goes further and
emits the runtime as well, producing a PLC that expects isolated CPU cores and
SCHED_FIFO scheduling to meet its cycle.
undoStudio — Where the ST Source Comes From
undoStudio is the IDE the pipeline starts in: the editor for IEC 61131-3 Structured Text, the project layout the compiler is pointed at, and the diagnostics the compiler produces read back against the source that caused them.
-
One editor panel for every kind of file. A
.st, a.json, a.cppand a configuration file all open above the same tab bar, because a document on screen is a document on screen whichever extension it has. - Semantic editing. Structured Text is parsed as it is edited, so the colouring, the outline and the member/statement/signature completion lists come from the symbol table the compiler itself builds — a name that resolves to nothing is left uncoloured, which is how a typo shows up before it is compiled.
-
The project is JSON.
project.json,plcs.json,tasks.json, one file per task and the PROGRAM order per PLC: the same versioned data st2cpp imports and exports, which is what lets the IDE and the compiler agree without either owning a format of its own. -
Diagnostics belong to a line. A
.stfile is not edited as one blob — it is regenerated from the panes being edited, and a source map maps each generated line back to the editor that owns it, so a diagnostic reported against the generated file still points at what the user typed. -
No domain functionality in the core. The IDE owns the lifecycle, the
window, the ImGui framework and the project on disk; everything else is an
undoApp loaded from
plugins/. A third party adds a panel without the core changing, and the PLC, fieldbus and diagnostics panels the runtime will need are the same kind of thing.
Legacy Migration via undoImport
undoImport will ingest existing commercial IDE configurations — TwinCAT (.tmc/.tpy) and CODESYS projects — extracting POU logic and variable declarations, then routing them directly into the st2cpp pipeline.
This makes undoRT immediately useful for teams with existing automation code, without requiring a full manual rewrite.
