The Python Scripts configuration page defines Python scripts that run inside EnvisionSCADA using an embedded Python 3.12 runtime. Each script entry stores the Python code directly and makes it available for execution from schedule actions, alarm actions, event actions, and external API calls.
Scripts have full read and write access to all runtime variables through built-in helper functions. Variables can be read and written by name using GetStringValue, GetIntValue, GetFloatValue, GetBoolValue, and SetValue. The script receives the current variable dictionary and writes back any changed values after the script completes. A script that ends with sys.exit() or sys.exit(0) completes normally and its values are written; any other exit code, such as sys.exit(1), ends the run with an error and its values are not written. A script with Network & File Access writes device variables and Local variables that count as license points only, see Network & File Access below.
Scripts can be made modular. A script marked as a module is imported and reused by other scripts (write shared functions, classes, and constants once), and any script can run another whole script as a step with RunScript. See Modules and reuse and Calling other scripts with RunScript below.
Name scripts according to their function. The name is used to reference the script from actions and appears in system status variables at runtime.
Script Settings
Create a new script with the Add button in the toolbar. The Add Python Script dialog contains the script settings organized in the sections General, Module and Network & File Access, with default values already filled in. Enter at least the Name and press Create. The script code itself is written afterwards in the Python Editor.
Each Python Scripts row represents one script. The row shows Enable, Name, Asset, User Groups, EndPoint, Network & File Access, and Description directly and these values can be edited in place. The script code is edited by opening the Python Editor using the editor button on the row.
Press the Settings icon on a script row to open the Python Script Settings dialog, which contains the same sections and all settings of the script. Press Save to apply the changes. The script code stays unchanged when settings are saved in this dialog and is always edited in the Python Editor.
The Python Editor shows the same settings next to the code in its Script Settings view, see Python Editor below.
| Setting | Description | Example | Units / Format |
|---|---|---|---|
| Enable | Enables or disables this script. | true | Boolean |
| Name | Unique script name. Referenced from actions in alarms, events, and schedules, and used in runtime status variables. For a module, the name is also the import name and must be a valid Python identifier (letters, digits, and underscores; not starting with a digit). | CalculateTotals | Text |
| Asset | Asset path for logical grouping. | ProductionLine1 | Text |
| User Groups | Comma-separated user groups allowed to execute this script. When empty, only Administrators can execute it. | Operators,Engineers | Text |
| EndPoint | URL path that registers this script as an external API endpoint. External systems can POST to this path to execute the script and receive its output. Requires Network & File Access: an EndPoint receives data from outside EnvisionSCADA, so a call to a script without it is refused. Entering an EndPoint on the Python Scripts page selects Network & File Access, which stays selected until the EndPoint is cleared. | /API/ProcessSensorData | URL path |
| IsModule | Marks this script as an importable module instead of a runnable script. Modules are not scheduled or triggered on their own: they are excluded from the Execute Script action pickers (alarms, events, schedules) and cannot be executed via an EndPoint. Other scripts reuse a module with import ModuleName. A module that defines fetch() can be the Module Script of a History Source; see the History Sources article. | false | Boolean |
| Network & File Access | Allows this script to open network connections, serial ports and files outside the project. Enable it on a script that obtains data from outside EnvisionSCADA, for example from a device, a web service or a file of another program. The script can then only write device variables and Local variables that count as license points. It is also required for a script with an EndPoint, and stays selected while an EndPoint is set. See Network & File Access below. A module runs with the rights of the script that imports it, so the setting applies to runnable scripts. | false | Boolean |
| Description | Free text description for documentation purposes. | Calculates daily production totals | Text |
| Script | Python 3.12 source code executed when the script is triggered. Edited in the Python Editor. | Python code |
No rows match the filter.
Python Editor
The Python Editor opens with the editor button on a Python Scripts row. It consists of a toolbar, an activity bar on the left that opens the side views Explorer, System Functions, Variables, History and Script Settings, the open scripts as tabs above the code, a bottom panel with Problems, Output, Writes and Run Log, and a status bar.
Several scripts and modules can be open at the same time, each in its own tab. A dot on the tab, after the script name in the toolbar and in the Explorer marks unsaved changes. Save and Execute apply to the script of the active tab. Closing a tab with unsaved changes asks to save it first, and Return to Python Scripts asks Save PythonScript? when an open script has unsaved changes.
The toolbar contains the following buttons. Each button shows its function and shortcut as a tooltip.
| Button | Function | Shortcut |
|---|---|---|
| Menu | Opens the configuration side menu. | |
| Script name | Shows the script of the active tab, followed by a dot when it has unsaved changes. Opens Script Settings. | |
| Save | Saves the script of the active tab and adds a version to History. The script is checked first, see Validation When Saving a Script. | Ctrl+S |
| History | Opens the History view. | |
| Undo, Redo | Undoes or redoes the last edit. | Ctrl+Z, Ctrl+Y |
| Find | Opens the find bar at the top of the code, with Match Case, Match Whole Word, Use Regular Expression and the number of matches. | Ctrl+F |
| Replace | Opens the find bar with the replace field. Replace All replaces every match in one step that Undo reverts. | Ctrl+H |
| Toggle Line Comment | Adds or removes # in front of the selected lines. | Ctrl+/ |
| Collapse all, Expand all | Collapses every outer block of the script, or expands all blocks again. | |
| Functions | Opens the System Functions view. | |
| Variables | Opens the Variables view. | |
| Preview | Switches Enable preview on or off. With it on, the files a run produces open in the Preview pane to the right of the code. Drag the edge of the pane to change its width. | |
| Focus mode | Opens the AI chat with the editor beside it. | |
| Auto-execute | Executes the active script after the AI assistant updated it. | |
| Commands | Lists every editor command by name with its shortcut. Type to filter the list, Enter runs the selected command. | Ctrl+Shift+P, F1 |
| Execute | Runs the code of the active tab with the saved Network & File Access setting of the script. | F5 |
| Theme | Opens the list of themes: Light, Dark, Blue Light and Blue Dark. The selected theme applies to the whole application, as the Theme button in the left menu of the pages, and the code colors follow it. | |
| Return to Python Scripts | Returns to the Python Scripts page. |
No rows match the filter.
The activity bar opens the side views. Pressing the button of the open view again closes the side bar, as does Ctrl+B.
| View | Content |
|---|---|
| Explorer | All Python scripts grouped by Asset, followed by the Modules. The dot in front of a script shows System.PythonStatus of its triggered runs: Idling, Running, Error or TimedOut. Icons mark Network & File Access and an EndPoint, and a disabled script is shown struck through. A click opens the script in a tab. Ctrl+P opens a script by name. |
| System Functions | The script functions with their parameters and description, grouped by purpose. A click inserts the call at the cursor. |
| Variables | All variables of the project, grouped by the part of the name before the first dot, with type and current value. A click inserts the read function that fits the type, for example GetFloatValue("Pump1_Speed"), or only the name when the cursor is inside the quotes of a variable function. |
| History | The saved versions of the script of the active tab, newest first, with date and user. Compare shows the differences between a version and the code in the editor, with Load this version and Close compare. Load puts the version in the editor; Undo restores the previous code and Save keeps the version. Compare with saved compares the unsaved changes with the last saved version. |
| Script Settings | Enable, Asset, UserGroups, EndPoint, Description, Importable module and Network & File Access of the script of the active tab, stored with Save. Entering an EndPoint selects Network & File Access, which stays selected until the EndPoint is cleared. The Name is changed on the Python Scripts page. Runtime shows System.PythonStatus, System.PythonMsg and System.PythonExecutionId of the script. Find usages under Used by lists the schedules, alarms, events, pages, reports and scripts that refer to the script. |
No rows match the filter.
The code is colored by type: Python keywords, strings, numbers and comments, the script functions and the variable names each have their own color. Lines changed since the last save are marked next to the line numbers. A block is collapsed and expanded with the arrow next to its line number.
Completion opens while a name is typed, and with Ctrl+Space. Inside the quotes of GetFloatValue, GetIntValue, GetBoolValue, GetStringValue and SetValue, and inside [[ ]], it lists the variables of the project with type and current value. Elsewhere it lists the script functions, the module scripts and the names used in the script, and after the name of a module script and a dot it lists the functions and constants of that module. Enter or Tab inserts the selected entry.
While the cursor is inside the parentheses of a script function, its parameters are shown above the line with the current parameter underlined. Hovering over a variable name shows its type, value with units, status and the time of the last update. Hovering over a script function shows its description. Ctrl+click on the name of a module script opens the module in a tab, and Ctrl+click on a variable name lists the references of that variable at the top of the Variables view.
The editor checks the code while it is typed. A finding is shown with a wavy underline in the code, a marker next to the line number and a mark on the scrollbar, and is listed in Problems. A click on a problem moves the cursor to its line.
| Check | Shown as |
|---|---|
| A variable name in a variable function or in [[ ]] that is not a variable of the project | Warning with the closest existing name. Change to replaces the name. A name that is built at runtime, for example "%s_Level" % tank, is not checked. |
while True: or while 1: | Error. Save refuses the script with the same message. |
| RunScript with a module, or with a script that does not exist | Warning. |
| import of a runnable script | Warning: only a script with Importable module can be imported. |
| [[Variable]] in a module | Error. Save refuses the module with the same message. |
| A network, serial port or file call in a script without Network & File Access | Hint that Execute stops at that call. |
No rows match the filter.
The bottom panel shows the results. Drag its top edge to change the height, use the arrow to maximize it and Ctrl+J to close and reopen it.
| Tab | Content |
|---|---|
| Problems | The checks of the active script and the errors of its last Execute and Save, with the line number where it is known. |
| Output | Per Execute the time, the script and Network & File Access, followed by the print() output and the result with the duration. |
| Writes | Every variable the last Execute of the active script wrote, with the value and the result: Written, Not written, or Refused by Network & File Access. Writes of a script started with RunScript show that script. |
| Run Log | Every Execute since the editor was opened, with time, script, duration, result and the number of variables written. |
No rows match the filter.
The status bar shows System.PythonStatus of the active script, whether it is a Runnable script or a Module, Network & File Access (a click opens Script Settings), the number of errors and warnings (a click opens Problems), the time and duration of the last Execute, the cursor position, and Unsaved changes or the date and user of the last save.
The editor has the following keyboard shortcuts.
| Shortcut | Function |
|---|---|
| F5 | Execute |
| Ctrl+S | Save |
| Ctrl+Shift+P, F1 | Commands |
| Ctrl+P | Go to Script... |
| Ctrl+F, Ctrl+H | Find, Replace. Enter and Shift+Enter move to the next and the previous match. |
| Alt+G | Go to Line... |
| Ctrl+Space | Show Suggestions |
| Ctrl+/ | Toggle Line Comment |
| Tab, Shift+Tab | Indent or outdent the selected lines by four spaces. |
| Ctrl+B | Toggle Side Bar |
| Ctrl+J | Toggle Panel |
| Ctrl+click | Opens a module script, or lists the references of a variable. |
| Esc | Closes completion, the find bar, the command list or the compare view. |
No rows match the filter.
Focus mode opens the AI chat beside the editor, which then fills the window to the left of the chat. When the AI assistant updates a script that is open in a tab, the tab is reloaded with the new code. With Auto-execute selected, the active script is executed after the update. Both buttons are shown when the AI assistant is enabled.
Validation When Saving a Script
A script is checked when it is saved, in the Python Editor and through the API. A script that could never finish is refused, so the mistake is visible immediately instead of occupying a script thread at runtime. The Python Editor also shows the check while the code is typed, see Python Editor.
- A script body may not contain
while True:orwhile 1:. Saving one reports that infinite loops are not allowed. Lines that are commented out are ignored by this check. - Run a script repeatedly by triggering it from a Schedule at the required interval, rather than by looping inside the script.
Script Functions
The following built-in functions are available inside every Python script. In a module, import the ones you use from the envision module (see Modules and reuse).
| Function | Description | Example |
|---|---|---|
| GetStringValue(variable) | Returns the current value of a variable as a string. | GetStringValue("PLC1.Pump1_Speed") |
| GetIntValue(variable) | Returns the current value of a variable as an integer. A BOOL variable returns 1 for True and 0 for False. | GetIntValue("PLC1.Pump1_RunCount") |
| GetFloatValue(variable) | Returns the current value of a variable as a float. A BOOL variable returns 1.0 for True and 0.0 for False. | GetFloatValue("PLC1.Pump1_Speed") |
| GetBoolValue(variable) | Returns the current value of a variable as a boolean: the value of a BOOL variable (True or False), and True for any non-zero numeric value. | GetBoolValue("PLC1.Pump1_Running") |
| SetValue(variable, value) | Writes a value to a variable. The write is applied after the script completes, and a target that cannot be written is reported as an error on the run. A boolean is stored as 1 for True and 0 for False, so read it back with GetFloatValue or GetBoolValue rather than comparing it to the text "True". | SetValue("Local.TotalRuns", 42) |
| ModbusWriteMany(mapping) | Writes several Modbus tags in one batched operation. Keys are variable names, values are converted to strings. Writes are grouped per device, and a key that reaches no device is reported as an error on the run. | ModbusWriteMany({"PLC1.Setpoint": 50, "PLC1.Mode": 1}) |
| SampleGroup(name) | Triggers a sample group flush to the historian. | SampleGroup("Fast") |
| GetParam(key, default) | Returns a parameter passed to the script by an external API caller. Available when the script is triggered via the EndPoint; returns the default otherwise. | GetParam("body", "") |
| RunScript(name) | Runs another runnable script by name as a step and returns True on success. Targets scripts, not modules. Protected against cycles and excessive call depth. See Calling other scripts with RunScript. | RunScript("GenerateDailyReport") |
| SendEmail(to, subject, body, attachments, cc, bcc, html) | Sends an email through the SMTP server configured on the Parameters page. Only to and subject are mandatory. Recipients accept a single address or a list, and attachments accepts a list of file paths. See Sending email from a script. | SendEmail(to="operator@plant.com", subject="Daily Report", body="See attachment.", attachments=["DailyReport.csv"]) |
| RunReport(name, period_start, period_end, params) | Renders a report defined on the Reports page and returns the path of the file it produced. Leave the period arguments empty to use the period configured on the report. The produced file can be passed straight to SendEmail. | RunReport("Daily Production") |
| GetProjectFolder() | Returns the full path of the offline project folder, the Offline section of the Project page. This is the folder for files the server reads or writes: a SQLite database a report table queries, a CSV a script imports, or a file a script produces. Reaching this folder requires Network & File Access, so a script without it receives the path but cannot open files there. Build the file path with os.path.join instead of writing an installation path into the script. | os.path.join(GetProjectFolder(), "HourlyLog.db") |
| GetReportsFolder() | Returns the folder rendered reports are written to when the report has no Output Path of its own. | os.path.join(GetReportsFolder(), "Daily Production_2026-09-16.pdf") |
No rows match the filter.
Write targets are checked when the writes are applied. If a variable named by SetValue or ModbusWriteMany does not exist, has no owning device, or belongs to a device that is not loaded, or a ModbusWriteMany value does not fit the variable type, for example text for a REAL, the run finishes with the error Variable not written (not found, not writable, a value that does not fit its type, or its device is not loaded) followed by the variable names. The Execute view shows this next to the result, and for a configured script the status variables report it as described in Runtime Status Variables.
The check applies to the write itself, not to the rest of the script. The script has already run to completion at that point, so every other statement, including writes to valid variables, has taken effect. Reading a variable that does not exist stops the script immediately instead, with an error naming the variable.
Modules and reuse
Enable the IsModule setting to turn a script into a module. A module is not scheduled or triggered on its own; instead other scripts reuse its code with a standard Python import. This lets you write shared functions, classes, and constants once and import them from many scripts.
Modules are compiled once and cached by the runtime, so importing them adds no per-execution overhead. This is the recommended way to share logic between scripts, including scripts that run frequently.
# Reusable helpers. Import from any script with: import mathutils
# Keep modules stateless: define functions, classes, and constants only.
def scale(value, factor=2):
return value * factor
def clamp(value, lo, hi):
return max(lo, min(hi, value))
PI = 3.14159# A module must import the helpers it uses from the envision module.
from envision import GetFloatValue, SetValue
import mathutils
def scaled_tag(tag, factor=2):
return mathutils.scale(GetFloatValue(tag), factor)
def copy_scaled(src_tag, dst_tag, factor=2):
SetValue(dst_tag, scaled_tag(src_tag, factor))Because a module is imported rather than triggered, it must use the helper functions (GetFloatValue, SetValue, and so on) and it must import the helpers it uses from the envision module. The helper functions are only auto-injected into top-level runnable scripts, not into imported modules. Keep modules stateless: module-level variables are shared across all scripts and executions.
import mathutils
from envutils import copy_scaled
# Clamp a live tag using a shared helper and store the result
speed = mathutils.clamp(GetFloatValue("PLC1.Pump1_Speed"), 0, 100)
SetValue("Local.PumpSpeedClamped", speed)
# Read, scale, and write in one shared helper
copy_scaled("PLC1.Flow", "Local.FlowScaled", 1.5)Editing and saving a module takes effect immediately. Scripts that import it pick up the new code on their next execution, without restarting the server.
Calling other scripts with RunScript
Use RunScript(name) to run another whole script as a step. This is useful for orchestration: sequencing or composing existing scripts. The called script runs immediately, and any variable writes it makes take effect before control returns to the caller. RunScript returns True when the called script completes successfully.
# A supervisor script that composes existing scripts
print("Collecting sensor data...")
RunScript("CollectSensorData")
print("Generating the daily report...")
RunScript("GenerateDailyReport")
print("Done.")RunScript targets runnable scripts, not modules (use import for modules). The runtime protects against loops: if a script calls itself directly or indirectly (for example A calls B and B calls A), the call is refused with a clear error and the engine keeps running. A maximum call depth also applies as a safeguard.
Sending email from a script
SendEmail sends an email through the SMTP server configured on the Parameters page. The script does not hold any server details of its own: the SMTPServer, SMTPTLSPort, SMTPUsername, SMTPPassword and SMTPSender parameters are read at the moment the mail is sent, so changing them takes effect without editing scripts. The sender address is always the configured SMTPSender.
from datetime import datetime
start_time = datetime.now()
filename = "TankLevelReport.csv"
with open(filename, "w") as report:
report.write("Timestamp,Tank,Level\n")
report.write("%s,Tank1,%s\n" % (start_time, GetFloatValue("Tank1_Level")))
email_body = f"Attached is the daily tank level report for {start_time.strftime('%Y-%m-%d')}."
SendEmail(
to='operator@plant.com',
subject='Daily Tank Level Report',
body=email_body,
attachments=[filename]
)| Argument | Description | Default | Example |
|---|---|---|---|
| to | Recipient address, or a list of addresses. Mandatory. | to=["operator@plant.com", "shift@plant.com"] | |
| subject | Subject line. Mandatory. | subject="Daily Tank Level Report" | |
| body | Message body. | empty | body="See the attached report." |
| attachments | List of files to attach. Relative paths are resolved against the application folder. | no attachments | attachments=["DailyReport.csv"] |
| cc | Address, or list of addresses, in copy. | none | cc="supervisor@plant.com" |
| bcc | Address, or list of addresses, in blind copy. | none | bcc=["audit@plant.com"] |
| html | Set to False to send the body as plain text. | True | html=False |
No rows match the filter.
A common use is to distribute a file the script has just produced. A relative attachment path is resolved against the application folder, which is where a script writes when it opens a file by name only. An absolute path is also accepted.
The mail is sent before the call returns, so the script can rely on the mail having left the system on the next line. When the mail cannot be sent, SendEmail raises an error describing the reason, for example an empty SMTPServer parameter, an attachment that does not exist, or a mail server that refuses the message. The script then stops, its System.PythonStatus_ScriptName variable becomes Error and the reason is placed in System.PythonMsg_ScriptName, so the failure can be shown on a page or used in an alarm condition. Every attempt is also written to the system log. Wrap the call in try and except when the script must continue after a failed send.
try:
SendEmail(to='operator@plant.com', subject='Line stopped', body='Line 1 has stopped.')
except Exception as problem:
SetValue('MailStatus', str(problem))Up to 10 files can be attached to one mail, with a combined size of at most 20 MB.
Network & File Access
Network & File Access separates scripts that calculate from scripts that obtain data from outside EnvisionSCADA. A script without Network & File Access works with what EnvisionSCADA provides: it reads and writes variables with the script functions, reads the Historian database, renders reports, sends email, and creates temporary files with the tempfile module. The calculation packages numpy, pandas, scipy, scikit-learn, statsmodels, numexpr, bottleneck, matplotlib, Pillow, kiwisolver, contourpy, PyYAML, MarkupSafe and charset-normalizer are available to every script.
Opening a network connection or a serial port, opening a file or a database other than the Historian database (the Configuration database included), reaching the project folder, starting another program or a thread, and loading a native library or another compiled package require Network & File Access. A script without it stops at that point with the error Network & File Access required: ... was blocked. Tick Network & File Access on script ScriptName. The System Log records the message once per script.
Python scripts open the EnvisionSCADA databases Configuration.db, Historian.db, StandbyHistorian.db and Local.db read-only, with and without Network & File Access. This also applies when a database is reached through ATTACH, VACUUM INTO or a URI with mode=rw. Queries, pandas.read_sql and temporary tables work as before. A statement that writes to one of these databases stops with attempt to write a readonly database. Configuration and history change only through EnvisionSCADA itself.
A script without Network & File Access opens no database other than Historian.db, also not through ATTACH; an ATTACH of another database stops with unable to open database. Reports also read Configuration.db and the databases in the project folder, as before.
| Message | Meaning |
|---|---|
| Network & File Access: this script can only write device variables and External Source variables. | First sentence of every refusal. One sentence per reason follows, naming up to 10 variables. |
| Not written, not an External Source variable: Name. Tick External Source on these Local variables. | A Local variable without External Source. |
| Not written, managed by a UDT: Name. Tick External Source on the UDT variable member and push. | A Local variable created by a UDT instance. External Source is set on the Local variable member in the UDT Manager. |
| Not written, array element: Name. Tick External Source on the array Name. | An element of a Local variable array. External Source is set on the first element and applies to every element. |
| Not written, the variable is disabled: Name. | A disabled device variable or Local variable. |
No rows match the filter.
A script with Network & File Access runs in the access worker, a separate Python process that EnvisionSCADA starts with the first run of such a script. It uses the same Python installation, libraries, script functions and module scripts. Its values reach EnvisionSCADA through SetValue, ModbusWriteMany and assignments to [[Variable]] references, and these writes are limited to device variables and Local variables with External Source enabled. External Source variables count as license points, see the Local Variables article. A write to any other variable is refused while the other writes of the run take effect, and the run reports the refused variables:
A STRING Local variable with External Source enabled and a STRING device variable can only be read by scripts with Network & File Access and by reports. Reading one from another script stops that script with Name is supplied by an External Source and can only be read by scripts with Network & File Access. or Name is a STRING device variable and can only be read by scripts with Network & File Access. This rule keeps several values from being combined in one text and split into Local variables without External Source. A script that parses a device text, for example a barcode, a batch ID or a JSON payload, needs Network & File Access, and the Local variables it writes need External Source.
Writing a STRING device variable stays possible without Network & File Access: with SetValue, or with a [[Variable]] reference that is only assigned, for example [[PLC1.Text]] = "abc". A reference that is also read, for example with +=, is refused.
- Scripts with Network & File Access share imported module scripts and their module-level variables with each other, separate from the scripts without it. A module script imported by both is loaded once in each.
- The Variables dictionary is read-only. Write values with SetValue.
- Connections to the HTTP and HTTPS ports, the OPC UA server port and the Modbus TCP server port of EnvisionSCADA itself are refused. Write variables with SetValue.
- The Module script of a History Source of Type Script runs with Network & File Access, in the access worker. See the History Sources article.
- Network & File Access itself costs no license points. Only the External Source variables the script writes do.
Further properties of a script with Network & File Access:
Network & File Access in the Python Editor
Execute in the Python Editor runs the code in the editor with the saved Network & File Access setting of the script. Two actions set the checkboxes from there, both offered in a bar above the code after the run.
Network & File Access required is shown when the script was stopped because it needs Network & File Access. It names the blocked operation after Blocked. Enable enables Network & File Access on the script, and Enable and run again also executes the script again.
When a script with Network & File Access wrote Local variables without External Source, the bar names the number of variables that were not written, and the Writes tab lists them as Refused by Network & File Access. Its button opens the Mark as External Source dialog, which lists the variables that were not written:
| Element | Description |
|---|---|
| Variable list | Local variables that can be marked here, with Type and Points, all selected. An array is listed as the range of its elements and counts one point per element. A STRING variable shows Access scripts only. |
| Can't be ticked here | Variables that are set elsewhere: a Local variable managed by a UDT, set on the UDT variable member and pushed (Open UDT Manager opens the UDT Manager), and a disabled variable. |
| Current Point Count | The point count after Apply, as current points + selected points = total, of the License Point Count, with Within the License Point Count or Exceeds the License Point Count. When the count exceeds the License Point Count, the license becomes invalid and the server switches to DEMO mode. In this version External Source variables are counted but not yet part of the license check: the line shows the count a future version checks, and exceeding it has no effect yet. |
| Apply | Enables External Source on the selected variables. |
| Apply and run again | Enables External Source on the selected variables and executes the script again. |
| Cancel | Closes the dialog without changes. |
No rows match the filter.
Projects from an Earlier Version
In earlier versions every script could open network connections and files. After the update, a script that obtains data from outside EnvisionSCADA stops with Network & File Access required until Network & File Access is enabled on it, and the Local variables it writes keep their last value until then. Execute each such script in the Python Editor and use the two dialogs above, or enable Network & File Access on the Python Scripts page and External Source on the Local Variables page. For a large number of variables, export the Local Variables, set the ExternalSource column to True on the rows the scripts write and import the file again. Each External Source variable counts as a license point. In this version they are counted but not yet part of the license check, and the License page shows the point count a future version checks: when it exceeds the License Point Count, update the license or contact support before installing that version. A script that is called through its EndPoint is refused until Network & File Access is enabled on it.
Runtime Status Variables
EnvisionSCADA exposes the following system variables for each script at runtime. Replace ScriptName with the configured script Name.
| Variable | Description |
|---|---|
| System.PythonStatus_ScriptName | Current execution status of the script. Values: Running, Idling, Error, TimedOut. TimedOut indicates the script exceeded the configured Python Script Execution Timeout and is still running. The status returns to Idling or Error once the script completes. See Execution Timeout. |
| System.PythonMsg_ScriptName | Last error or status message produced by the script. On a timeout it holds the timeout message that names the script and the elapsed threshold. |
| System.PythonExecutionId_ScriptName | Random integer updated each time the script starts, useful for detecting new executions. |
No rows match the filter.
Execution Timeout
A watchdog monitors how long each script runs, including a run started with Run in the Python editor. The threshold is set by the Python Script Execution Timeout parameter in the Engineering Settings section of the Parameters page. The value is in seconds and defaults to 600. Set it generously, because report generation and similar tasks can legitimately take minutes. Set it to 0 to disable the watchdog.
When a script runs longer than the configured timeout and is still executing, the runtime logs an entry to the System Log, sets System.PythonStatus_ScriptName to TimedOut, and writes a descriptive message to System.PythonMsg_ScriptName that names the script and the elapsed threshold. The runtime then interrupts the script with a KeyboardInterrupt. A script that is stopped this way ends with status Error, and System.PythonMsg_ScriptName reports that the script exceeded the execution timeout and was stopped. Reports are supervised the same way, with System.ReportStatus_ReportName and System.ReportMsg_ReportName. The interrupt takes effect at the next Python statement and is repeated every 2 seconds until the script ends, so a script that catches KeyboardInterrupt is interrupted again; when it still ends on its own, System.PythonMsg_ScriptName reports that it exceeded the execution timeout and its status is Error. When a script started with RunScript is stopped, the calling script receives the same KeyboardInterrupt and stops as well. A script that waits inside a call of a compiled library can keep running. A run in the Python editor that exceeds the timeout is stopped with the same message in the output pane. The script keeps its busy state while it remains running, so the same script is not started again on top of a run that has not finished. Once the script completes, its status returns to Idling or Error.
A script with Network & File Access is interrupted at the timeout as well. When it has not stopped 15 seconds later, for example because it waits on a network connection without a timeout, the access worker is restarted. This stops every script with Network & File Access running at that moment, and each of them reports Error with a message that names the script that exceeded the timeout. Set a timeout on network calls.
Scripts and reports that are started automatically, by a schedule, an alarm, an event, or UDT logic, are executed by a fixed set of runtime workers. If a firing arrives while an earlier firing of the same action is still waiting to start, the new firing is skipped and an entry is written to the System Log. This keeps a script that is running long from building up a backlog of earlier firings that would then all run one after another. Execution started by an operator or an external system, from the Reports page, the Python Editor, the API and MCP, is not queued and is never skipped.
Because the status is published per script as a runtime variable, you can raise an alarm on it. Create an alarm whose Condition monitors the status variable of the script you want to supervise. The alarm activates when the script is flagged as timed out and clears when the script status changes to Idling or Error.
System.PythonStatus_GenerateDailyReport == "TimedOut"Notes
Scripts must execute once and terminate. A script body containing while True: or while 1: is refused when the script is saved. For repetitive execution, trigger the script from a schedule at the required interval.
State between scheduled runs must be stored in local variables (using SetValue), not in Python global variables. Python global state is not preserved between executions. Module-level variables are shared and must not be used for per-run state.
A module (IsModule enabled) is importable but not runnable. It does not appear in the Execute Script action pickers for alarms, events, and schedules, and it cannot be executed via an API endpoint. Its name must be a valid Python identifier, and it accesses variables through the helper functions imported from the envision module.
When EndPoint is configured, external systems can POST to POST /API/{EndPoint} with a JSON body. The body is available inside the script as GetParam("body"). The script output is returned as the API response. The script must have Network & File Access, because the body is data from outside EnvisionSCADA. A call to a script without it returns Network & File Access required: an EndPoint receives external data. Tick Network & File Access on script ScriptName. and sets System.PythonStatus_ScriptName to Error. With Network & File Access the script runs in the access worker, so the values it writes reach device variables and Local variables that count as license points only.
User Groups controls which operator groups can execute the script from the runtime or via the API. When User Groups is empty, only Administrators can execute the script.
When the AI assistant is used from the Python Editor, the name of the script the editor was opened with is provided as page context. The assistant can use the existing Python Scripts retrieval tool to load the script list, identify the matching Name, and work with the Script source code directly from the active editor context.