Run a PROGRAM Locally¶
This guide shows you how to compile and run a LoLa PROGRAM on your local
machine, exchange data with it via the process image files, and read the
schema manifest.
Prerequisites¶
lolainstalled (pip install -e .in the repo root, oruv run lola)rustc(1.70 or later) in PATH —lola runcompiles a Rust runner binary- Python 3.10 or later for the plant simulator
1. Write (or use) a PROGRAM¶
A PROGRAM is a non-instantiable root POU that declares the process image
interface: VAR_INPUT for fields the runtime reads from the input image, and
VAR_OUTPUT for fields the runtime writes to the output image.
PROGRAM Counter
VAR_INPUT
reset : BOOL;
END_VAR
VAR_OUTPUT
count : INT;
END_VAR
IMPLEMENTATION
count:
SET 0 WHEN reset PRIO 10;
SET count + 1 OTHERWISE;
END_IMPLEMENTATION
END_PROGRAM
The Pasteurizer example (examples/pasteurizer/Pasteurizer.lola) is a more
complete PROGRAM that composes a BatchPasteurizer SSFC with a TemperaturePID
FB using only a WIRING block.
2. Compile and check before running¶
lola examples/pasteurizer/Pasteurizer.lola
This runs the full verification chain: type check, Z3 proof obligations,
and SSFC invariant analysis. Fix any errors before proceeding to lola run.
3. Start the runner¶
lola run examples/pasteurizer/Pasteurizer.lola --period-ns 100000000
This:
- Compiles the PROGRAM to a Rust binary (cached in
~/.cache/lola/rust/). - Writes the manifest:
/tmp/pasteurizer.io.json. - Creates the input image if it does not exist:
/tmp/pasteurizer-input.img. - Launches the runner and starts the scan loop at 100 ms period.
You should see:
info: wrote process image manifest: /tmp/pasteurizer.io.json
info: created zeroed input image: /tmp/pasteurizer-input.img (47 B payload)
lola run: Pasteurizer period=100.0 ms ∞ scans
input -> /tmp/pasteurizer-input.img
output -> /tmp/pasteurizer-output.img
manifest -> /tmp/pasteurizer.io.json
The runner runs until you press Ctrl-C.
4. Read the manifest¶
The manifest describes every process image field by name:
cat /tmp/pasteurizer.io.json
{
"abi_version": 1,
"program": "Pasteurizer",
"schema_hash": "0x...",
"representation_profile": "FLOAT64_REPR",
"inputs": [
{"name": "temperature", "semantic_type": "MATHREAL",
"encoding": "IEEE754_BINARY64_LE", "offset": 0, "size": 8},
...
],
"outputs": [...]
}
Any client that wants to read or write the process images should load the manifest and look up fields by name, not by hard-coded offsets.
5. Connect a plant simulator¶
In a second terminal:
python examples/pasteurizer/plant.py
The plant simulator:
- Reads
/tmp/pasteurizer.io.jsonto discover field offsets. - Opens the input and output image files.
- Validates the
schema_hashin the image header against the manifest. - Runs the plant physics loop and drives one complete batch.
You will see real-time output as the batch progresses through Fill → Heat → Hold → Drain → Completed.
6. Write your own client¶
Use ManifestReader from examples/pasteurizer/plant.py as a starting
point, or implement the protocol yourself using the manifest JSON:
import struct, mmap, json
with open("/tmp/pasteurizer.io.json") as f:
schema = json.load(f)
# Build a name → (offset, size, struct_fmt) lookup
_FMT = {"IEEE754_BINARY64_LE": "d", "BOOL_U8": "?",
"INT16_LE": "h", "INT32_LE": "i", "INT64_LE": "q",
"UINT16_LE": "H", "UINT32_LE": "I", "UINT64_LE": "Q"}
inputs = {f["name"]: (f["offset"], f["size"], _FMT[f["encoding"]])
for f in schema["inputs"]}
# Write a field into a bytearray payload
def write_field(buf, name, value):
off, sz, fmt = inputs[name]
struct.pack_into("<" + fmt, buf, off, value)
# Example: set temperature = 25.3°C
payload = bytearray(sum(f["size"] for f in schema["inputs"]))
write_field(payload, "temperature", 25.3)
write_field(payload, "start", True)
See Process Image Reference for the full binary format and seqlock protocol.
Custom image paths¶
If /tmp is not suitable (e.g. macOS with SIP restrictions, or you want
persistent image files):
lola run my.lola \
--input /dev/shm/my-input.img \
--output /dev/shm/my-output.img \
--manifest /dev/shm/my.io.json
The plant simulator accepts matching --input, --output, and --manifest
arguments.
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
rustc compilation failed |
rustc not in PATH or version too old | Install rustc ≥ 1.70 |
schema_hash mismatch |
Input image file is stale | Delete the image files and restart lola run |
manifest not found |
lola run not started yet |
Start lola run first; it writes the manifest before the runner starts |
| Plant output stays zero | Input image not yet written | Check the input path; plant writes the image on each scan |
PROGRAM not found error |
Source file contains a FUNCTION_BLOCK |
Use lola run only with PROGRAM blocks |
| Array type error | A VAR_INPUT/VAR_OUTPUT is an array type | Array fields are not yet supported in lola run |
See also¶
- Process Image Reference — binary format and manifest schema
- CLI Reference — lola run
- Compose FB and SSFC Components — how to write a PROGRAM with WIRING
- Language Reference §3.6 — PROGRAM syntax