Until this month, a device started from a bench saved into its server's working folder. The run's journal sat on one disk, its raw files on another, and its log in the bench's home directory. Now every bench knows the lab's shared disk. Each device writes into that bench's folder on it, and the run folder holds everything about the measurement: configuration, data, log, report, who took it and why. Any machine can open it, and it is still there after the bench that recorded it is uninstalled.

plesty-lib 0.6.0 — released plesty-bench 0.5.0 — released

Three folders, one name each

The bench passes each device three environment variables. A device module reads them and never has to work out a path of its own:

Bench folder
DEVICE_DATA_ROOT

This bench's folder inside the share. It is created when missing, and every device the bench starts writes below it.

Device folder
DEVICE_DATA_PATH

The device's default folder: its fleet data_dir, or tmp/<device> when the fleet names none.

Paths are stored relative to the share, never as absolute paths. Each machine mounts the share somewhere different, so a path in a run's records only means something once the reading machine joins it to its own mount point.

For users: set it up once per bench

1 · Tell the bench where the share is

The Windows installer has a page for it, and you can skip it and set it later. In the console it is Server ▸ Configure… (Plesty share and Bench folder). From the command line:

# this bench
plesty-server data-path SHARE --folder FOLDER
plesty-server data-path            # show what is set
plesty-server data-path --clear

# another bench, from your machine
plesty-bench -b BENCH data-path SHARE --folder FOLDER

The share must be mounted. The folder is a path inside it and is created if it does not exist. A fleet data_dir that is absolute or uses .. to climb out of the folder is refused. Before a share is set, a bench still gives its devices a folder under its home, data/, so a single PC with no share can still run the demo.

Override order
A device's own .env and the fleet's env: still override the folder the bench picks. An experiment's setup.yaml no longer does: where a device writes is decided by the bench that runs it.

2 · Create a run: say who and why

Create run now asks for an Operator, which is required and pre-filled with whoever created the last run on this bench, and an optional Note. Both are stored in the run folder next to the data:

The run table has two new columns, Operator and Note (the first line of the latest note). A duplicated run keeps the original's operator.

3 · Name it, configure it, without touching the module

4 · Read the log anywhere, after anything

When a run's job ends, however it ends, the bench copies the job's log into the run folder as job-logs/<job>.log, next to the report. When the bench showing a run has no job for it, the Log pane reads that copy. So the log is there for a run measured on another bench, or after an uninstall emptied the bench home. Runs measured before this version have no copy.

What a run folder holds

FileWhat it is
plan.jsonThe frozen plan and configuration the run started with
journal.jsonlThe step-by-step history, appended while the run goes
records.jsonl · data/The experiment's records and the data they point to
raw/<device>/Files each device wrote, routed there by the run
created.json · notes.txtWho created the run, and the notes added to it
job-logs/ · reportThe run's own log and its measurement report
Your own analysis
The bench keeps the simple analysis that runs during the measurement, and replay and the run report reuse it. There is deliberately no standard "re-analyse" button. Detailed analysis is yours to write in Python with your own methods and plots, and because the run folder is complete and on the share, you can open it from any machine:
from plesty.lib.experiment import Run, latest_run

run = Run(latest_run("/mnt/plesty/pol-pl", name="pol_pl"))
cfg = run.config                                   # what it was run with
for record in run.records():
    path = run.local(record["data_file"])        # share-relative → this machine's mount

Run.local finds the share from data_root=, then PLESTY_DATA, then the path the recording machine wrote in the journal, if that path exists here.

A ~ in PLESTY_DATA or a run root now means this machine's home directory (plesty-lib 0.6.0). Before, it was taken literally and the run landed in a folder named ~.

For developers: route devices, record share-relative paths

plesty-lib 0.6.0 changes the device and experiment side to match. The changes are breaking. Every item below is in the plesty-lib changelog with its migration.

A device writes only inside its bench folder

set_data_path(path) now takes a path relative to DEVICE_DATA_ROOT. It refuses an absolute path, a drive letter or ~, collapses .. and refuses anything that resolves outside the folder. It returns the folder relative to the share, for example bench-a/pol-pl/<run>/raw/cam, rather than an absolute path. If you subclass BaseDeviceSyncModel you get this without writing anything: take the three variables from the environment and let the base class check paths.

An experiment routes its devices, then records what they answered

async def setup(self) -> None:
    await super().setup()
    if self.raw_dir is not None:            # None when this computer has no PLESTY_DATA
        # replaces devices.set_data_path(self.raw_dir, subdirs=True, devices=[...])
        self.route(self.devices, ["spec"])

# in a step: keep the device's file as a share-relative path, and return it to be recorded
data_file = self.raw_file("spec", reported_path)   # → bench-a/pol-pl/<run id>/raw/spec/<name>
Migrating a module
Pass the run folder relative to the bench folder, read what set_data_path returns as relative to the share, and replace any call to set_data_path(self.raw_dir, …) in an experiment with self.route(...) in setup(). The plesty-bench 0.5.0 line requires plesty-lib>=0.6.0.

plesty-lib 0.6.1 makes a run's end match its record. A run the operator stopped used to print "Run completed" and exit 0. Now python -m <module> ends the way the run's journal does: a canceled run prints "Run canceled" and exits 130, as Ctrl-C does, and an aborted run prints "Run aborted" and exits 1. Both name the --resume that continues it, and @cli.finished runs only for a completed run.

Trying it without a share

To test the whole flow without a share, set PLESTY_DATA to a temporary folder and DEVICE_DATA_ROOT to a folder inside it. Then set_data_path("run-1/raw/cam") returns <bench folder>/run-1/raw/cam, and set_data_path("/abs") raises ValueError. The full guide is plesty-lib's Experiment ▸ Shared disk page.