Skip to content

Capstones

Capstones show source, runtime config, HMI files, and operator surfaces working together in one larger project.

Plant Demo

Docs category: docs/public/examples/capstones.md

This tutorial teaches how to navigate and debug a multi-file ST project in VS Code.

What You Learn

  • Type + FB + Program + Configuration layering
  • Cross-file navigation/refactor workflow
  • Debugging a state machine through scan cycles
  • Project config role (trust-lsp.toml)

Project Structure

  • src/types.st: shared enums/structs
  • src/fb_pump.st: PumpController state machine
  • src/program.st: orchestration logic (PlantProgram)
  • src/config.st: CONFIGURATION, TASK, program binding
  • trust-lsp.toml: indexing/profile settings for editor/runtime features

Step 1: Open and Build

code examples/plant_demo
trust-runtime build --project examples/plant_demo --sources src
trust-runtime validate --project examples/plant_demo

Step 2: Cross-File Navigation (Exact Keystrokes)

  1. Open src/program.st.
  2. Hold Ctrl and click PumpController -> lands in src/fb_pump.st.
  3. Press Alt+Left to go back.
  4. Place cursor on SpeedSet, press F2, enter PumpSpeedSet.
  5. Confirm rename preview includes all impacted references.
  6. Right-click PumpState -> Find All References (or Shift+F12).
  7. Verify references appear across multiple files.

Step 3: Debugger Walkthrough

  1. Open src/fb_pump.st.
  2. Set a breakpoint inside CASE Status.State OF.
  3. Press F5 (uses .vscode/launch.json).
  4. In Runtime Panel, toggle %IX0.0 (start signal).
  5. Step through transitions (Idle -> Starting -> Running).
  6. Inspect Variables panel and inline values for Status.State and Ramp.

Step 4: Understand Configuration Relationship

Read src/config.st and map the hierarchy:

  • CONFIGURATION defines deployment root.
  • TASK defines scan interval/priority.
  • PROGRAM ... WITH TASK binds logic execution.
  • VAR_CONFIG binds symbols to %I/%Q addresses.

This is the runtime wiring contract for your typed logic model.

Step 5: Guided Change Exercise

  1. In src/fb_pump.st, change:
  2. RampTime : TIME := T#1s; -> T#2s
  3. Re-run debug.
  4. Observe longer time spent in Starting before Running.

Troubleshooting

  • If F5 fails:
  • confirm trust-debug path in .vscode/settings.json.
  • If no cross-file symbols:
  • confirm workspace root is examples/plant_demo.
  • If no runtime change on input toggles:
  • confirm correct %IX/%IW addresses from src/config.st.

OpenOT Multi-PROGRAM Logging

This project is the canonical OpenOT workload for every supported persistence backend. The Structured Text is identical for every run; select the database only by choosing the corresponding TOML file.

The example logs:

  • a templated message with four typed arguments and process, operating-mode, ISA-88, and PackML state transitions;
  • BOOL, every supported signed and unsigned integer width, REAL, LREAL, and bounded STRING values;
  • on-change, REAL deadband, periodic, and REAL hysteresis sampling declarations;
  • an audited setpoint change with actor, reason, authorization, unit, and semantic role;
  • alarm and interlock activation/clear plus acknowledgement, confirmation, shelving, suppression, service state, comment, reset, and priority change;
  • recipe load/approval, material addition, and batch state;
  • operator action, login, logout, security failure, and electronic signature.

There are no SQL calls or OpenOT opcodes in the application programs. truST generates and drains these producer instances into one serialized ring:

Filler.OotProducer
BatchControl.OotProducer
OperatorAudit.OotProducer
SignatureAudit.OotProducer
TypedValues.OotProducer
ConditionLifecycle.OotProducer

examples/openot_multi_program/openot-coverage-manifest.json is the machine-readable inventory binding each event family, value type, sampling policy, state model, condition class, message argument, and database product to this one workload. The integration gate rejects a manifest for any other pinned OpenOT revision or with an incomplete top-level inventory.

Select a backend

Product Configuration Secret environment variables
SQLite runtime.toml none
PostgreSQL runtime.postgresql.toml TRUST_OPENOT_DATABASE_URL
TimescaleDB runtime.timescaledb.toml TRUST_OPENOT_DATABASE_URL
MySQL runtime.mysql.toml TRUST_OPENOT_DATABASE_URL
MariaDB runtime.mariadb.toml TRUST_OPENOT_DATABASE_URL
SQL Server runtime.sqlserver.toml TRUST_OPENOT_DATABASE_URL
InfluxDB 3 runtime.influxdb3.toml TRUST_OPENOT_INFLUX_HOST, TRUST_OPENOT_INFLUX_TOKEN

MySQL and MariaDB intentionally use the same backend = "mysql" adapter but have separate runnable configurations and real-product verification.

To run a non-default configuration, copy the selected file to a temporary project copy as runtime.toml; do not paste a password or token into TOML. For example:

cp -a examples/openot_multi_program /tmp/trust-openot-postgresql-example
cp /tmp/trust-openot-postgresql-example/runtime.postgresql.toml \
  /tmp/trust-openot-postgresql-example/runtime.toml
trust-runtime build --project /tmp/trust-openot-postgresql-example --sources src
trust-runtime run --project /tmp/trust-openot-postgresql-example

For SQLite, run the checked-in default directly:

trust-runtime build --project examples/openot_multi_program --sources src
trust-runtime run --project examples/openot_multi_program
sqlite3 examples/openot_multi_program/history/trust-logging.sqlite3 \
  'SELECT event_name, COUNT(*) FROM event_log GROUP BY 1 ORDER BY 1;'

The build emits openot-definition.json beside the bytecode. Persistence uses that exact definition to resolve the ring records into canonical OpenOT event, loss, and placeholder documents. The database checkpoint advances in the same durable transaction as its documents. See the public OpenOT database persistence guide for backend setup, TLS, queries, restart, backup, and outage behavior.

This is a deliberately broad conformance workload, not a production scan-time template. It instruments many event families in one resource and can take a long time per VM scan in an unoptimized development build. Measure the smaller set of attributes required by the real machine against its cycle-time budget; database commits remain on the separate host persistence thread.