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 that cannot be read, or that takes longer than the timeout, raises 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.
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.
export_csv returns as soon as the export finishes, but the workspace currently
stays frozen a moment longer. A second call made straight away is refused with
busy, and in the Python and NodeJS SDKs the refusal is not raised: the call
returns normally and no file appears.
workspace.export_csv("/tmp/packets.csv", source="packets", instances=[i2c.instance_id])
workspace.export_csv("/tmp/samples.csv", source="samples", channels=[0, 1])
# packets.csv exists. samples.csv does not, and nothing was raised.
Until this is fixed, export once per run where you can. Otherwise leave a pause between calls and check that each file exists before relying on it.
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}', { mark_dirty: false });
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.
Errors: file operations raise IoError (Error::Io in Rust,
ss_status_Failed with ss_last_error() in C) for a path the server cannot
write, a disk that filled up, or a cancellation.