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 trends, statistics and point-in-time values |
system.vars | Gateway variables — per project, or global |
system.files | Read and write files in the workspace uploads folder |
system.message | Send on the message bus to pipelines and screens, or ask and wait for a reply 1.0.8 |
system.script | Run a Gateway entry point server-side — from a browser, or from a pipeline Python node 1.0.8 |
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.
The editor
The Python editor lints as you type, so syntax errors surface while you are writing rather than when the pipeline fires at 03:00.
The Problems panel New in 1.0.6 lists what the linter found — message, severity, rule id
and Ln 4, Col 7 — and clicking a row goes there. The footer used to show a bare count, which told
you there was a problem and then declined to say what; the only route to the actual fault was to
notice a squiggle somewhere in a scrolled file and hover it. Every editor's diagnostics arrive
through the same panel, whether they came from the Python linter, TypeScript or CSS.
One font size, everywhere. New in 1.0.6 The − / + control sets the size for every
code editor in the Designer — project scripts, event and binding scripts, named queries, CSS and
the pipeline node editor — and it persists, so turning it up to read a long script does not have to
be done again in the next panel. The AI chat and the assistant panel have their own, scoped to the
message text so the header and controls stay put.
Changed in 1.0.8 The code editors, the AI Assistant panel and the AI chat widget default to 14 px (it was 16). On upgrade a stored size of 16 becomes 14 — the old default was saved for everyone, so it cannot be told apart from a choice — and any other size is kept.
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 |