plesty.lib.experiment.control
Driving a run that has already started, from inside it or from another process.
A run is a process of its own: the console that started it cannot call a method
on it. So the control surface is a file in the run directory — control.json,
written by whoever is driving and read by the run itself. The run directory is
already the whole interface in this direction (a watcher tails journal.jsonl
and records.jsonl and asks the run nothing), and a bench already stops a
device server with a file rather than a signal, because Windows has no SIGINT
to send. No socket, no bound port, and it crosses a master-to-node relay for
free, since whoever runs the commands runs them on the host holding the files.
The step is the unit. A request takes effect at a step boundary, which is
where the journal already commits: a step is atomic, so there is no coherent
place inside one to stop. abort() is the exception and says so — it
interrupts the step in flight, which is the point of having it.
from plesty.lib.experiment import control
control.pause(run_dir) # stop before the next step
control.report(run_dir, "helium top-up") # tell the run what happened
control.resume(run_dir)
control.cancel(run_dir) # finish this step, then stop
The writers are plain functions because the caller is usually not holding an
Experiment — it is a console in
another process with nothing but the path.
Attributes
Classes
What the driver is currently asking of the run. |
|
Where a run reads what is being asked of it. |
|
A control surface held in memory, for a run driven in its own process. |
|
A control surface in a run directory, for a run driven from elsewhere. |
Functions
|
Return what is currently being asked of the run in run_dir. |
|
Ask the run to hold before its next step. |
|
Let a held run go on. |
|
Ask the run to finish the step in flight and then stop. |
|
Ask the run to stop without finishing the step in flight. |
|
Tell the running run something worth recording. |
|
Remove the control file, so the run is asked for nothing. |
|
Return the parsed control document, or an empty one. |
|
Apply changes to the control file and return the resulting state. |
Module Contents
- plesty.lib.experiment.control.CONTROL_FILE = 'control.json'
- plesty.lib.experiment.control.CANCEL = 'cancel'
- plesty.lib.experiment.control.ABORT = 'abort'
- plesty.lib.experiment.control.POLL_SECONDS = 0.5
- class plesty.lib.experiment.control.ControlState
What the driver is currently asking of the run.
- Variables:
paused – Whether the run should hold before its next step.
reports – Every report written so far, oldest first. The run consumes them by count, so a report is journaled once however often the file is read.
reason – Why the stop was asked for, when the driver said.
- paused: bool = False
- stop: str | None = None
- reports: Sequence[str] = ()
- reason: str = ''
- property stopping: bool
Whether a stop of either kind has been asked for.
- Return type:
bool
- to_dict() dict[str, Any]
Return the state as JSON primitives.
- Return type:
dict[str, Any]
- classmethod from_dict(raw: Mapping[str, Any]) ControlState
Rebuild a state from a control document.
A malformed field takes its default rather than raising: this document is read on every step of a long measurement, and a run must not die because something wrote nonsense into it.
- Parameters:
raw (Mapping[str, Any]) – The parsed document.
- Returns:
The state it describes.
- Return type:
- class plesty.lib.experiment.control.Control
Bases:
ProtocolWhere a run reads what is being asked of it.
- read() ControlState
Return what the driver is asking for now.
- Return type:
- class plesty.lib.experiment.control.MemoryControl
A control surface held in memory, for a run driven in its own process.
A notebook or a script that holds the experiment calls the methods directly; nothing is written to disk, so a run nobody drives from outside pays no file read per step.
Start with a run that is asked for nothing.
- state
- read() ControlState
Return the state as last set.
- Return type:
- pause() None
Ask the run to hold before its next step.
- Return type:
None
- resume() None
Let a held run go on.
- Return type:
None
- cancel(reason: str = '') None
Ask the run to finish the step in flight and then stop.
- Parameters:
reason (str)
- Return type:
None
- abort(reason: str = '') None
Ask the run to stop without finishing the step in flight.
- Parameters:
reason (str)
- Return type:
None
- report(text: str) None
Add a report for the run to journal.
- Parameters:
text (str)
- Return type:
None
- class plesty.lib.experiment.control.FileControl(run_dir: str | os.PathLike[str])
A control surface in a run directory, for a run driven from elsewhere.
Reading is deliberately forgiving: a missing file means nothing is being asked, and an unparseable one is treated the same way. The file is written by another process and may be read mid-write, and a measurement must not end because a read landed between a truncate and a flush — the next step reads it again a moment later.
Watch the control file of run_dir.
- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
- path
- read() ControlState
Return what the file asks for, or a clear state when it says nothing.
- Return type:
- plesty.lib.experiment.control.state(run_dir: str | os.PathLike[str]) ControlState
Return what is currently being asked of the run in run_dir.
- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
- Returns:
The control state; a clear one when no control file exists.
- Return type:
- plesty.lib.experiment.control.pause(run_dir: str | os.PathLike[str]) ControlState
Ask the run to hold before its next step.
The rig stays connected and the instruments keep their positions — that is the whole difference from stopping and resuming, which releases the rig and pays a reconnect and a preflight to come back.
- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
- Returns:
The state now asked for.
- Return type:
- plesty.lib.experiment.control.resume(run_dir: str | os.PathLike[str]) ControlState
Let a held run go on.
- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
- Returns:
The state now asked for.
- Return type:
- plesty.lib.experiment.control.cancel(run_dir: str | os.PathLike[str], reason: str = '') ControlState
Ask the run to finish the step in flight and then stop.
Nothing is lost and nothing is redone: the step commits,
teardownruns and the journal recordsrun_canceled, so a resume starts at the next step. The cost is waiting for the step — which on a long exposure or a magnet ramp is real; that is whatabort()is for.- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
reason (str) – Why, journaled with the event.
- Returns:
The state now asked for.
- Return type:
- plesty.lib.experiment.control.abort(run_dir: str | os.PathLike[str], reason: str = '') ControlState
Ask the run to stop without finishing the step in flight.
The operation in flight is interrupted and
teardownruns, so the instruments are left safe rather than mid-acquisition. The step never committed, so a resume re-runs it from the beginning.- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
reason (str) – Why, journaled with the event.
- Returns:
The state now asked for.
- Return type:
- plesty.lib.experiment.control.report(run_dir: str | os.PathLike[str], text: str) ControlState
Tell the running run something worth recording.
The run journals it where it happened — between two steps — so a helium top-up or a realignment is on record beside the data it explains, and reaches the measurement report rather than the operator’s memory.
- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
text (str) – What to record.
- Returns:
The state now asked for.
- Return type:
- plesty.lib.experiment.control.clear(run_dir: str | os.PathLike[str]) None
Remove the control file, so the run is asked for nothing.
- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
- Return type:
None
- plesty.lib.experiment.control._read(path: pathlib.Path) dict[str, Any]
Return the parsed control document, or an empty one.
- Parameters:
path (pathlib.Path) – The control file.
- Returns:
The document;
{}when it is absent, unreadable or not a mapping.- Return type:
dict[str, Any]
- plesty.lib.experiment.control._amend(run_dir: str | os.PathLike[str], **changes: Any) ControlState
Apply changes to the control file and return the resulting state.
The file is replaced rather than rewritten in place, so a run reading it sees either the old document or the new one and never half of each.
- Parameters:
run_dir (str | os.PathLike[str]) – The run’s checkpoint directory.
**changes (Any) – Fields of the control document to set.
- Returns:
The state the file now asks for.
- Return type: