Headless datalogger: leave and rejoin
Start a capture that runs for hours, let the script exit, and come back later to collect the results. Nothing has to stay connected in between.
This works because the server owns the workspace. A disconnect destroys nothing: the capture keeps running, the data stays reachable, and any client can attach to it later. It is also what lets a test bench hand a session from one script to the next.
Starting and walking away​
- Python
- NodeJS
- Rust
- C
from ikalogic_scanastudio import ScanaStudio
def start(device: str = "se254") -> int:
with ScanaStudio.connect() as server:
workspace = server.create(device)
workspace.capture.start(samples=2_000_000_000, sample_rate=25_000_000)
print(f"workspace {workspace.id} is capturing; this script can now exit")
return 0
import { ScanaStudio } from '@ikalogic/scanastudio';
async function start(device = 'se254') {
const server = await ScanaStudio.connect();
try {
const workspace = await server.create(device);
workspace.capture.start({ samples: 2_000_000_000, sample_rate: 25_000_000 });
console.log(`workspace ${workspace.id} is capturing; this script can now exit`);
return 0;
} finally {
server.close();
}
}
use scanastudio_client::{CaptureRequest, ScanaStudio, DEFAULT_URL, Result};
async fn start(device: &str) -> Result<()> {
let server = ScanaStudio::connect(DEFAULT_URL).await?;
let workspace = server.create(device).await?;
workspace.capture().start(&CaptureRequest::new(2_000_000_000, 25_000_000))?;
println!("workspace {} is capturing; this program can now exit", workspace.id());
Ok(())
}
#include <stdio.h>
#include "scanastudio.h"
int main(void) {
ss_server *server = ss_server_connect(NULL);
if (!server) return 1;
ss_workspace *workspace = ss_server_create(server, "se254");
if (!workspace) return 1;
/* ss_capture_start arms and returns; ss_capture_run is the one that blocks. */
ss_capture_start(workspace,
"{\"samples\": 2000000000, \"sample_rate\": 25000000}");
printf("workspace %llu is capturing; this program can now exit\n",
(unsigned long long)ss_workspace_id(workspace));
/* Free the handle, NOT close the workspace: closing would end the capture. */
ss_workspace_free(workspace);
ss_server_disconnect(server);
return 0;
}
Note what does not happen here: no wait(), and no close(). Closing the
ScanaStudio client ends the connection; the workspace goes on capturing
without it.
Coming back for the results​
- Python
- NodeJS
- Rust
- C
from ikalogic_scanastudio import ScanaStudio
def rejoin() -> int:
with ScanaStudio.connect() as server:
sessions = server.workspaces()
if not sessions:
print("the server holds no workspace")
return 1
for session in sessions:
print(
f" {session.workspace_id} {session.device_name} {session.state} "
f"{session.last_sample} samples {session.clients} client(s)"
)
workspace = server.attach(sessions[0].workspace_id)
print(f"\nattached to {workspace.id}, now at {workspace.capture.last_sample}")
workspace.capture.stop()
print(f"stopped at {workspace.capture.wait()}")
workspace.save("/data/overnight.scana")
workspace.close()
return 0
import { ScanaStudio } from '@ikalogic/scanastudio';
async function rejoin() {
const server = await ScanaStudio.connect();
try {
const sessions = await server.workspaces();
if (sessions.length === 0) {
console.log('the server holds no workspace');
return 1;
}
for (const session of sessions) {
console.log(
` ${session.workspace_id} ${session.device_name} ${session.state} ` +
`${session.last_sample} samples ${session.clients} client(s)`,
);
}
const workspace = await server.attach(sessions[0].workspace_id);
console.log(`attached to ${workspace.id}, now at ${workspace.capture.last_sample}`);
workspace.capture.stop();
console.log(`stopped at ${await workspace.capture.wait()}`);
await workspace.save('/data/overnight.scana');
workspace.close();
return 0;
} finally {
server.close();
}
}
use scanastudio_client::{ScanaStudio, DEFAULT_URL, Result};
use std::time::Duration;
async fn rejoin() -> Result<()> {
let server = ScanaStudio::connect(DEFAULT_URL).await?;
let sessions = server.workspaces().await?;
let Some(first) = sessions.first() else {
println!("the server holds no workspace");
return Ok(());
};
for session in &sessions {
println!(" {} {} {} {} samples {} client(s)",
session.workspace_id, session.device_name, session.state,
session.last_sample, session.clients);
}
let workspace = server.attach(first.workspace_id).await?;
workspace.capture().stop()?;
let last = workspace.capture().wait(Duration::from_secs(300)).await?;
println!("stopped at {last}");
workspace.save("/data/overnight.scana", Duration::from_secs(600)).await?;
workspace.close()?;
Ok(())
}
char *sessions = ss_server_workspaces(server); /* JSON array */
printf("%s\n", sessions);
ss_string_free(sessions);
/* Parse out a workspace_id with your JSON library of choice, then: */
ss_workspace *workspace = ss_server_attach(server, workspace_id);
if (!workspace) fail("attach");
printf("now at %lld samples\n", (long long)ss_capture_last_sample(workspace));
ss_capture_stop(workspace);
ss_workspace_save(workspace, "/data/overnight.scana", 600.0);
ss_workspace_close(workspace);
ss_workspace_free(workspace);
Example output. The listing, then the rejoin:
192139071722857 SE254 (demo) done 1000000 samples 0 client(s)
192139071722863 SE254 (demo) sampling 1225000000 samples 0 client(s)
attached to 192139071722863, now at 1225000000
stopped at 1225000000
0 client(s) on a sampling session shows that nothing was watching it, and it
kept going anyway.
attach replays the workspace's full state, so once connected you see the
capture configuration, the decoders, the annotations and the log.
Notes for your own bench​
Size the capture, not the timeout. samples is what bounds the run. Do not
try to make a long capture work by raising a timeout: start() and leave.
Find your workspace by a stable key. The example attaches to sessions[0].
On a server holding several, match on device_name, or on the id you printed
when you started:
mine = next(s for s in server.workspaces() if s.device_name == "SP259 (sn 1004250000604)")
workspace = server.attach(mine.workspace_id)
clients tells you who else is watching. A workspace with zero clients is
not at risk. A workspace with one may be open in somebody's ScanaStudio window,
and closing it takes their session away too.
Stop before you read. A capture still running gives you a moving
last_sample. stop() then wait() settles it.
Check whether it is still running first. wait() waits for a state change.
If you attach to a workspace that is already done and call wait(), it blocks
until the timeout. Check capturing or state first:
session = next(s for s in server.workspaces() if s.workspace_id == wanted)
workspace = server.attach(session.workspace_id)
if session.capturing:
workspace.capture.stop()
last = workspace.capture.wait(timeout=60)
else:
last = workspace.capture.last_sample # already finished; just read it
workspaces() is the reliable place to read this: it reports the workspace's
state as the server sees it, before you attach.
The server never closes a workspace on its own. A capture you start and forget will still be there tomorrow, holding memory and a device. Whatever starts a workspace should have something that eventually closes it.
The same mechanism lets one script arm a capture, a second collect it, and a third export it. All they share is a workspace id.