Script library
The decoder, builder and pattern generator scripts installed on the server.
Reached through server.scripts (server.scripts() in Rust; in C, the
ss_scripts_* and ss_script_* functions take the server handle).
It is shared by every workspace and every client. Installing a script makes it available to all of them, and deleting or renaming one takes it from all of them, including the ScanaStudio window.
Listing what is installed​
- Python
- NodeJS
- Rust
- C
scripts = server.scripts
for script in scripts: # also scripts.all(), len(scripts)
print(script.file_name, script.name, script.version)
scripts.find("pwm.js") # by file name or declared name, or None
scripts.decoders() # the ones that can decode
scripts.generators() # the ones that can drive the pattern generator
scripts.library_dir # the folder, on the SERVER's machine
const scripts = server.scripts;
for (const script of scripts) { // also scripts.all()
console.log(script.file_name, script.name, script.version);
}
scripts.find('pwm.js'); // undefined if there is none
scripts.decoders();
scripts.generators();
scripts.library_dir;
let scripts = server.scripts();
for script in scripts.all() {
println!("{} {} {}", script.file_name, script.name, script.version);
}
scripts.find("pwm.js"); // Option<ScriptDesc>
scripts.decoders();
scripts.generators();
scripts.library_dir();
char *all = ss_scripts(server); /* JSON array */
char *dir = ss_scripts_library_dir(server); /* on the server's machine */
printf("%s\n%s\n", all, dir);
ss_string_free(all);
ss_string_free(dir);
Fields of each script:
file_name: the file, relative to the library folder. This is whatdecoders.add(),pattern.add()andgeneration.add_builder()take.name,version,author,description,help_url: from the tags in the script's source.capabilities:decode,trigger,builder,builder_object,demoandpattern, one flag per kind of script the file provides.origin_url: where the server downloaded it from; empty for a script written locally.modified: the file was edited after it was downloaded, so an update would overwrite those edits.file_access: the script may open paths of its own (see set_file_access).
The online catalogue​
- Python
- NodeJS
- Rust
- C
report = server.scripts.sync() # timeout 300 s
catalogue = server.scripts.online() # refresh=True to fetch it again
names = server.scripts.install([catalogue[0].url])
const report = await server.scripts.sync(); // timeout_ms = 300_000
const catalogue = await server.scripts.online();
const names = await server.scripts.install([catalogue[0].url]);
server.scripts().sync(Duration::from_secs(300)).await?;
let catalogue = server.scripts().online(false).await?;
let names = server.scripts().install(&[catalogue[0].url.as_str()], false).await?;
ss_scripts_sync(server, 300.0);
char *catalogue = ss_scripts_online(server, false); /* JSON array */
char *names = ss_scripts_install(server, "[\"https://...\"]", false); /* JSON array */
sync()brings the library up to date with the online catalogue and waits for it to finish. A sync already running is joined rather than started again. It returns the report (how many scripts were downloaded, updated, skipped and failed) in Python and NodeJS.online()reads the catalogue without installing anything. Each entry hasname,version,author,url,description,help_urlandrelease_notes.install(urls, overwrite_modified=False)installs or updates catalogue entries, named by their download URL. A local file edited since its download is left alone unlessoverwrite_modifiedis set.
Adding and changing scripts​
| Call | What it does |
|---|---|
import_file(file_name, source) | Installs a script from its source text. |
import_url(url) | Downloads a script from a URL outside the catalogue. |
create(file_name, name=…, …) | Writes a new script from the template, with one stub per capability. |
rename(file_name, new_file_name) | Renames a script. |
duplicate(file_name, new_file_name) | Copies a script. Updates never overwrite the copy. |
set_file_access(file_name, enabled) | Lets a script open paths of its own. |
delete(file_names) | Deletes scripts. There is no recycle bin. |
usage() | How many instances use each script, across the open workspaces. |
Every call in the table except usage() returns the list of file names it
touched. When a name is already taken, the server writes the file under another
name, so read the returned name rather than assuming the one you asked for.
- Python
- NodeJS
- Rust
- C
names = server.scripts.create(
"my_bus.js",
name="My bus",
author="Bench team",
description="Decoder for our test bus",
decode=True, trigger=False, builder=False, pattern=False, demo=False,
)
server.scripts.duplicate("pwm.js", "pwm_bench.js")
for row in server.scripts.usage():
print(row.file_name, row.instances, row.workspaces)
const names = await server.scripts.create('my_bus.js', {
name: 'My bus',
author: 'Bench team',
description: 'Decoder for our test bus',
});
await server.scripts.duplicate('pwm.js', 'pwm_bench.js');
const usage = await server.scripts.usage();
use scanastudio_client::ScriptTemplate;
let template = ScriptTemplate::new("My bus")
.author("Bench team", "")
.description("Decoder for our test bus")
.decode(true);
let names = server.scripts().create("my_bus.js", &template).await?;
server.scripts().duplicate("pwm.js", "pwm_bench.js").await?;
let usage = server.scripts().usage().await?;
/* Every field of the template is optional; NULL makes a decoder. */
char *names = ss_script_create(server, "my_bus.js",
"{\"name\": \"My bus\", \"author\": \"Bench team\"}");
ss_string_free(names);
names = ss_script_duplicate(server, "pwm.js", "pwm_bench.js");
ss_string_free(names);
char *usage = ss_scripts_usage(server); /* JSON array */
ss_string_free(usage);
A .scana refers to its decoders by file name. After a rename, saved
workspaces that use the script no longer find it. Read usage() before you
rename or delete a script.
set_file_access​
By default a script can only open the files the user picks through its own file
fields. set_file_access(file_name, True) lets it open any path. It is off for
every script until you turn it on. A granted script can read and write anything
the account running the server can. The grant follows the file name, so a
renamed script and a duplicate both start without it.
Errors​
A library operation that fails raises ScriptOpError in Python and NodeJS. It
carries the server's message, skipped (files left alone because they were
edited since their download) and failed (files the operation could not
touch). In Rust it is Error::Io with the message. In C the call returns NULL
(or a failing ss_status for ss_scripts_sync) and ss_last_error() says why.
An unreachable catalogue makes sync() and online() fail the same way.
Default timeouts in Python and NodeJS: 300 s for sync(), 120 s for
online(), 180 s for the other operations.