Saving and exporting
Writing a workspace to disk: a .scana file that keeps everything, or a CSV of
one part of it. Both sit directly on the workspace.
Every path on this page is resolved by the server, on the server's machine and with the server's permissions, not by your script. Against a remote server, a local path means nothing and the file lands on the far end. See Remote and shared instruments.
save​
Writes the whole workspace to a .scana: the captured data, the decoders and
their configuration, the markers and the measurements.
- Python
- NodeJS
- Rust
- C
workspace.save("/data/run-42.scana", timeout=600.0)
workspace.path # where it was saved, or None
workspace.dirty # True when there are unsaved changes
// The SERVER writes this path, on the server's machine.
await workspace.save('/data/run-42.scana', { timeout_ms: 600_000 });
workspace.path; // where it was saved, or null
workspace.dirty; // true when there are unsaved changes
// The SERVER writes this path, on the server's machine.
workspace.save("/data/run-42.scana", Duration::from_secs(600)).await?;
workspace.path(); // where it was saved, or None
workspace.dirty(); // true when there are unsaved changes
/* The SERVER writes this path, on the server's machine. */
if (ss_workspace_save(workspace, "/data/run-42.scana", 600.0) != ss_status_Ok) {
fprintf(stderr, "save: %s\n", ss_last_error());
}
char *path = ss_workspace_path(workspace); /* NULL if it never saved */
if (path) { printf("saved to %s\n", path); ss_string_free(path); }
ss_workspace_dirty(workspace); /* true with unsaved changes */
Opening a .scana​
Reopen one later with
open(). No device needed.
open() returns once the capture is loadedopen() waits until the file is loaded. When it returns the capture is in the
workspace, so there is no polling loop to write:
- Python
- NodeJS
- Rust
- C
workspace = server.open("/data/run-42.scana")
workspace.capture.last_sample # the recording, not 0
const workspace = await server.open('/data/run-42.scana');
workspace.capture.last_sample; // the recording, not 0
let workspace = server.open("/data/run-42.scana").await?;
workspace.capture().last_sample(); // the recording, not 0
ss_workspace *workspace = ss_server_open(server, "/data/run-42.scana");
if (!workspace) fprintf(stderr, "open: %s\n", ss_last_error());
ss_capture_last_sample(workspace); /* the recording, not 0 */
A file the server cannot read raises at once rather than handing back an empty
workspace: IoError in Python and NodeJS, Error::Io in Rust, and a NULL
handle with ss_last_error() in C. A file that takes longer than the timeout to
load raises Timeout instead (Error::Timeout, ss_status_Timeout).
export_csv​
Writes part of the workspace as CSV.
- Python
- NodeJS
- Rust
- C
# Raw samples for two channels.
workspace.export_csv(
"/data/samples.csv",
source="samples",
channels=[0, 1],
)
# The decoded packets of one decoder.
workspace.export_csv(
"/data/packets.csv",
source="packets",
instances=[i2c.instance_id],
)
# A European-locale CSV, over a marker range.
workspace.export_csv(
"/data/run.csv",
source="hex",
separator=";",
decimal_comma=True,
from_marker=first,
to_marker=second,
)
// Raw samples for two channels.
await workspace.export_csv('/data/samples.csv', {
source: 'samples',
channels: [0, 1],
});
// The decoded packets of one decoder.
await workspace.export_csv('/data/packets.csv', {
source: 'packets',
instances: [i2c.instance_id],
});
// A European-locale CSV, over a marker range.
await workspace.export_csv('/data/run.csv', {
source: 'hex',
separator: ';',
decimal_comma: true,
from_marker: first,
to_marker: second,
});
use scanastudio_client::{decoded, samples};
// Raw samples for two channels. `samples()` builds the ExportOptions for you.
workspace
.export_csv("/data/samples.csv", &samples(vec![0, 1]), Duration::from_secs(600))
.await?;
// The decoded packets of one decoder. `decoded()` takes the source name.
workspace
.export_csv("/data/packets.csv", &decoded("packets", vec![i2c.instance_id]),
Duration::from_secs(600))
.await?;
/* Raw samples for two channels. The options are an ExportOptions, as JSON. */
ss_workspace_export_csv(workspace, "/data/samples.csv",
"{\"source\": \"samples\", \"channels\": [0, 1]}", 600.0);
/* The decoded packets of one decoder. */
char options[96];
snprintf(options, sizeof options,
"{\"source\": \"packets\", \"instances\": [%u]}", instance);
ss_workspace_export_csv(workspace, "/data/packets.csv", options, 600.0);
/* A European-locale CSV, over a marker range. */
ss_workspace_export_csv(workspace, "/data/run.csv",
"{\"source\": \"hex\", \"separator\": \";\","
" \"decimal_comma\": true, \"from_marker\": 1, \"to_marker\": 2}",
600.0);
The four sources:
source | What it writes |
|---|---|
samples | Raw logic samples for the channels you name. The largest by far. |
raw | The decoders' item rows: every element they produced, with its span. |
packets | The Packet View: decoded packets with title and content. |
hex | The Hex View: the byte stream the decoders produced. |
Options:
| Option | Meaning |
|---|---|
channels | Which channels to write. samples only. |
instances | Which decoder instances to write. The three decoded sources. |
separator | Field separator. Defaults to ,. |
decimal_comma | Write 0,5 rather than 0.5, for European spreadsheets. |
from_marker, to_marker | Restrict the export to a marker range. |
A CSV export freezes the workspace: any command that would change it is refused
with a Busy error until the export finishes. That includes commands from
other clients, so the ScanaStudio window will look stuck. Exporting a long
capture takes a while, which is why the default timeout is 600 s.
Each save(), export_csv() and open() waits for the answer to its own
request, so two exports issued one after the other both write their file. A
call made while an export, from this client or another, still holds the
workspace raises Busy (Error::Busy in Rust, ss_status_Refused in C)
instead of waiting.
save(), open() and export_csv() block until the server is finished and
cannot be cancelled. The timeout is the only way out: give the call a budget you
are willing to wait, and handle the Timeout if it runs over.
ui_state​
A workspace carries a blob of client view state (zoom, cursors, whatever your
own front end wants to remember). The server never reads it; it is written into
the .scana and comes back with it.
- Python
- NodeJS
- Rust
- C
workspace.set_ui_state('{"zoom": 4}', mark_dirty=False)
workspace.ui_state # the state this workspace was OPENED with
workspace.set_ui_state('{"zoom": 4}', false); // mark_dirty, positional
workspace.ui_state; // the state this workspace was OPENED with
workspace.set_ui_state("{\"zoom\": 4}", false)?;
workspace.ui_state(); // the state this workspace was OPENED with
ss_workspace_set_ui_state(workspace, "{\"zoom\": 4}", false);
char *state = ss_workspace_ui_state(workspace); /* NULL if there is none */
if (state) { printf("%s\n", state); ss_string_free(state); }
ui_state reports the state the workspace was opened with: the server sends
it when you attach and when a .scana is loaded. It is not an echo of
set_ui_state. A value you have just written reads back here only after the
workspace has been saved and reopened, or attached to afresh. Keep your own copy
in memory if you need to read it during a session.
Pass mark_dirty of false for a change the user would not call unsaved work,
such as moving a cursor rather than editing the capture.
In NodeJS, mark_dirty is a plain boolean second argument. An object passed
there is truthy, so set_ui_state(state, { mark_dirty: false }) marks the
workspace as modified.
Errors​
| Error | Python / NodeJS | Rust | C |
|---|---|---|---|
| The server could not write or read the file: a bad path, a full disk, a cancellation | IoError | Error::Io | ss_status_Failed |
Another export holds the workspace (save(), export_csv()) | Busy | Error::Busy | ss_status_Refused |
| The operation ran past its timeout | Timeout | Error::Timeout | ss_status_Timeout |
source is not one of samples, raw, packets, hex | ValueError / RangeError | Error::Io | ss_status_Failed |
The source check runs in the SDK before anything is sent. In Python it is a
plain ValueError and in NodeJS a RangeError, neither of which derives from
ScanaStudioError.
ss_last_error() holds the server's reason for each failure in C.