Scripting
QUBIQ exposes one API surface, system, in three execution contexts. The same call reads
identically in Python and JavaScript, so a script moves between contexts without being rewritten.
The three contexts
| Context | Language | Runs on | Use for |
|---|---|---|---|
| Pipeline Python | Python | The Python worker | Transformations inside a dataflow |
| Gateway scripts | Python | The gateway, server-side | Transactions, long work, anything a browser must not own |
| Client scripts | JavaScript | The operator's browser | Screen interaction, validation, UI logic |
Choosing
Two rules cover nearly every case:
- Anything transactional belongs on the gateway. A browser disconnect must not be able to strand an open transaction holding locks.
- Anything that must not be lost belongs in a pipeline. Pipeline writes are covered by store-and-forward; script writes are not.
The system API
| Namespace | Purpose |
|---|---|
system.tag | Read and write namespace tags |
system.db | SQL, named queries, stored procedures, transactions |
system.hist | Historian trend queries |
system.vars | Cross-pipeline shared variables |
system.files | Read and write files in the workspace uploads folder |
system.message | Push messages from the gateway down to browsers |
system.script | Run a Gateway entry point server-side (JavaScript only) |
Full signatures, per-method detail and worked examples: System API reference.
And context, in the browser
A browser script gets a second global, context — the view, its components, the live tag values
already on screen, the operator's session, and the shell around the view (panels, toasts, popovers).
The division is worth learning early: system.* crosses the wire and is permission-gated;
context.* does not and is not. A read of context.tag["..."] costs nothing because the screen
already has the value; await system.tag.read("...") is a round trip that asks the server.
The same code, both languages
result = system.tag.read("Line1/Filler/Motor1/Speed")
if result["quality"] == "Good" and result["value"] > 2800:
system.tag.write("Line1/Filler/Motor1/SpeedSetpoint", 2500)
const result = await system.tag.read("Line1/Filler/Motor1/Speed");
if (result.quality === "Good" && result.value > 2800) {
await system.tag.write("Line1/Filler/Motor1/SpeedSetpoint", 2500);
}
The method names are camelCase in both languages — deliberately, so a script reads the same wherever it runs.
Always check quality
r = system.tag.read("Line1/Tank1/Level")
if r["quality"] != "Good":
return None # do not act on an untrustworthy value
A Bad-quality read still returns a value — the last known one. Acting on it is how a dead sensor
drives a live process. This is the single most important habit in QUBIQ scripting.
Batch, do not loop
# ❌ N round trips
values = [system.tag.read(p) for p in paths]
# ✅ one
values = system.tag.readAll(paths)
The same applies to database work: one query returning many rows beats many queries returning one.
The sandbox
Scripts run in a restricted environment. The standard set (system, math, json, datetime) is
available; administrators can install additional Python packages, which land in a managed directory
on the worker's path.
Linting
The Python editor lints as you type, so syntax errors surface while you are writing rather than when the pipeline fires at 03:00.
Security
- Scripts run with the gateway's authority — a script can read any tag and any connection it names. Grant script-authoring permissions accordingly.
- Prefer named queries over string-built SQL. Where inline SQL is unavoidable, always parameterise.
- Do not put credentials in scripts or in variables. Use a connection's credential storage.
- Consequential actions are recorded in the audit journal.
In this section
| Page | Contents |
|---|---|
| System API reference | Every namespace, method and signature |
| Python nodes | Scripting inside a pipeline |
| Gateway scripts | Server-side entry points |
| Client scripts | Browser-side screen logic |
| The script sandbox | What is available, and how to extend it |