# Interface Contracts A **contract** here is a promise plesty-lib makes to the code built on it: what a class or function guarantees, and what a subclass must provide in return. Modules rely on these promises, so they change only with a new version number and a note in the changelog. Each entry below states the promise and links the page that shows how to use it. ## Devices **`BaseDeviceSyncModel`**, in `plesty.lib.device.base_device_sync`. Every device inherits this class. It combines three independent parts, each a separate base class (a *mixin*): - `ConfigSystem`, the parameters: declared, checked against their type and range ([Parameter System](../device/param_system.md)); - `FunctionSystem`, the operations: declared in code or in a JSON file ([Function System](../device/func_system.md)); - `TelemetrySystem`, a snapshot of the device's state (`status()`) and optional callbacks on every read and write ([Data & Metadata Schemas](../common/data_schemas.md)). A subclass implements `connect`, `disconnect`, `_write_`, `_query_`, `check_errors` and `check_operatability`. In return, `write` and `query` check the value, log the call, convert the reply, and report it to the callbacks. A `with` block connects and disconnects, and claims the instrument for this object while it is open. See [Base Device](../device/base_device.md). **`doc_model()`** returns a `DeviceDocModel`: everything a device declares, as data rather than text. Its parameters are grouped, its operations listed, and the methods every device has (`standard_methods`) are kept apart from the device's own. Anything that describes a device, such as generated documentation, the server's `describe` reply or a control panel, reads this instead of collecting the information itself. See [Base Device](../device/base_device.md). **Solvers**, in `plesty.lib.solver`. A `CmdSolver` turns a parameter into the command that reads or writes it; an `OpSolver` turns an operation request into the instrument's reply. `SCPISolver` also runs any operation whose whole exchange is one SCPI command and one value, taken from the `command` field of the operation's declaration, so such operations need no code. See [Command Solver](../device/cmd_solver.md). **`CompositeDevice.call`**, in `plesty.lib.device.composite_device`. When a call to a device on another computer times out, the client is rebuilt and the call tried again, a limited number of times, before the last `TimeoutError` is raised. See [Resilient remote calls](../device/composite_device.md#resilient-remote-calls). **Preflight**: checking that an instrument is really there before a measurement. A connection's `TrafficManager.preflight(address)` returns a `Preflight` result. A device's `preflight()` returns the same result as a plain dictionary without serial numbers, so that its server can send it over the network. A composite's `preflight()` returns a `Preflight` with one part per device, each including what that device's server reported. On a `Preflight`, `ready` says whether everything needed is there, `summary()` explains what is missing, and `to_dict()` gives the result without serial numbers, for saving. See [Preflight: is the instrument actually there?](preflight.md). ## Experiments **`Experiment`, `Plan` and the journal**, in `plesty.lib.experiment`. `build_plan()` returns a `Plan` of `Step`s, fixed once the run starts and identified by a fingerprint of its content (a SHA-256 hash). Before any device is touched, `run()` checks that every step names a public method of the experiment. Each step is recorded in an append-only journal. `run(resume=)` skips the steps already recorded and refuses to continue if the plan's fingerprint has changed. `teardown()` always runs, however the run ends. See [Experiment Framework](../experiment/index.md). **Run control**, in `plesty.lib.experiment.control`. A request written to the run folder's `control.json` (pause, resume, cancel, abort, or a note) takes effect at the next boundary between two steps. Only an abort is also checked during a step, every half second, and it interrupts the step. See [Asking a running measurement to pause or stop](data_flow.md#asking-a-running-measurement-to-pause-or-stop). **Result files**, in `plesty.lib.data.io`. `save_result` writes the data file first, and its description (`ResultDocument`, JSON) last. A description on disk therefore means its data is complete. `load_document` reads a description without loading the data; `convert_to_hdf5` packs several results into one HDF5 file. See [Data & Metadata Schemas](../common/data_schemas.md). ## Analyzers and reports **`Analyzer`**, in `plesty.lib.analyzer`. A subclass declares `input_schema` and `output_schema` and implements one synchronous `analyze()`. Calling the analyzer checks the inputs, fills in missing units and descriptions of the outputs from the schema, and stamps every array and table in the outputs with its provenance: the analyzer's class, its package version, and its settings. An analyzer never imports a device. See [Analyzer Framework](../analysis/index.md). **`Report`**, in `plesty.lib.report`. A report is a list of text, tables and figures. `save()` writes it as JSON; `Report.load()` reads it back and `to_html()` or `to_latex()` renders it, without importing the analyzer that made it. See [Reports](../analysis/reports.md). ## Viewing **`Viz`, `RunData` and `Plot`**, in `plesty.lib.monitor`. `Viz` makes the viewer for one experiment. `RunData` reads everything a run has saved, once, and every plot in the viewer shares it. A plot is a title and a function `draw(data)` that returns what to draw: `Line`, `Scatter`, `Image`, `Scalar` or `Text`. Viewing only reads the run folder, so it cannot disturb the measurement. The older `Monitor` classes still work. See [Viewing a Run](../viewing/index.md). **`Shell` and panels**, in `plesty.lib.ui`. A `Panel` is anything that fits in a sub-window; `Shell` is the main window that docks, floats and tabs them, remembers their layout, and refreshes all of them on one timer. Colours and styles come from packaged files, never from Python code. Only `plesty.lib.ui.qt` imports Qt. See [Shell Framework](../viewing/ui.md). ## Tests every module runs **Contract pipelines**, in `plesty.lib.test`. Each kind of module has a set of tests that checks it keeps these contracts, without the instrument: | Pipeline | For | Checks | Run by plesty-sdk as gate | |---|---|---|---| | `DevicePipeline` | devices | 8 tests: the declarations are valid, every parameter and operation works against a simulated instrument, connecting and disconnecting, identity, errors | `d1` | | `ExperimentPipeline` | experiments | 5 tests: the plan is the same every time, every step names a method, the plan survives saving and loading, the lifecycle methods are asynchronous | `e1` | | `AnalyzerPipeline` | analyzers | 5 tests: the schemas are valid, `analyze()` matches them, a round trip on generated inputs, provenance is stamped | `a1` | A **gate** is one of the checks `plesty check`, the checking command of plesty-sdk, runs before a module may be published. What a simulated instrument cannot show, such as the exact command format, a flaky connection or two devices sharing one computer, is tested on the real instrument by `FieldTestPipeline`, over the network by `client_field_test`, and for shared computers by `field_test_concurrency`. See [Testing](../testing/index.md).