plesty-lib 0.2.7__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/.gitlab-ci.yml +1 -1
- plesty_lib-0.2.9/CHANGELOG.md +42 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/PKG-INFO +3 -2
- plesty_lib-0.2.9/REUSE.toml +21 -0
- plesty_lib-0.2.9/docs/api_reference.md +19 -0
- plesty_lib-0.2.9/docs/contributing.md +28 -0
- plesty_lib-0.2.9/docs/data_schemas.md +89 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/base_device.md +45 -13
- plesty_lib-0.2.9/docs/device/composite_device.md +60 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/index.md +5 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/logging_system.md +5 -6
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/test_helper.md +89 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/traffic_manager.md +1 -1
- plesty_lib-0.2.9/docs/experiment.md +119 -0
- plesty_lib-0.2.9/docs/imgs/device_layers.svg +77 -0
- plesty_lib-0.2.9/docs/imgs/device_standard.svg +58 -0
- plesty_lib-0.2.9/docs/imgs/plesty_framework.svg +66 -0
- plesty_lib-0.2.9/docs/index.md +113 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/quickstart.md +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/toc.yaml +5 -2
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/gl-secret-detection-report.json +5 -5
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/__init__.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/data/__init__.py +9 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/data/array.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/data/ctype_manager.py +3 -4
- plesty_lib-0.2.9/plesty/lib/data/io.py +321 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/data/table.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/data/types.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/data/units.py +3 -4
- plesty_lib-0.2.9/plesty/lib/device/__init__.py +11 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/device/async_wrapper.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/device/base_device_sync.py +66 -12
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/device/base_tcp_scpi_device.py +5 -6
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/device/base_visa_scpi_device.py +5 -6
- plesty_lib-0.2.9/plesty/lib/device/composite_device.py +194 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/device/device_utils.py +3 -4
- plesty_lib-0.2.9/plesty/lib/device/doc.py +37 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/device/funcs.py +258 -8
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/device/params.py +17 -4
- plesty_lib-0.2.9/plesty/lib/device/telemetry.py +357 -0
- plesty_lib-0.2.9/plesty/lib/experiment/__init__.py +19 -0
- plesty_lib-0.2.9/plesty/lib/experiment/base_experiment.py +296 -0
- plesty_lib-0.2.9/plesty/lib/experiment/journal.py +113 -0
- plesty_lib-0.2.9/plesty/lib/experiment/schedule.py +123 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/service/__init__.py +16 -33
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/service/resource_manager.py +21 -21
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/service/tcp_ip_client.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/service/tcp_ip_server.py +11 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/sim/__init__.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/sim/data_generator.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/solver/__init__.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/solver/iceblock.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/solver/scpi.py +3 -4
- plesty_lib-0.2.9/plesty/lib/test/__init__.py +6 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/test/device_func_system.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/test/device_param_system.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/test/device_pipeline.py +3 -4
- plesty_lib-0.2.9/plesty/lib/test/experiment_pipeline.py +233 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/test/grouped_param_keys.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/test/resource_allocation.py +5 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/test/schema_params.py +5 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/traffic/__init__.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/traffic/serial.py +3 -5
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/traffic/tcp_ip.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/traffic/usb_utils.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/traffic/utils.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/traffic/visa.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/utils/__init__.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/utils/dll_utils.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/utils/error_utils.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/utils/logger.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/utils/registry.py +12 -6
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plesty/lib/utils/settings.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/plestylib/__init__.py +3 -5
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/pyproject.toml +5 -2
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_data_array.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_data_types.py +3 -2
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_device_base.py +139 -3
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_device_funcs.py +3 -3
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_device_params.py +3 -3
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_device_pipeline.py +3 -3
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_device_scpi.py +3 -3
- plesty_lib-0.2.9/tests/test_device_telemetry.py +230 -0
- plesty_lib-0.2.9/tests/test_experiment.py +313 -0
- plesty_lib-0.2.9/tests/test_experiment_pipeline.py +115 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_misc.py +3 -3
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_schema_params.py +3 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_service_manager.py +3 -2
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_settings.py +3 -2
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_sim.py +3 -2
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_solver.py +3 -2
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_tcp_ip_resources.py +44 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_traffic_drivers.py +3 -2
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_traffic_utils.py +3 -3
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/tests/test_utils.py +3 -3
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/uv.lock +96 -524
- plesty_lib-0.2.7/CHANGELOG.md +0 -17
- plesty_lib-0.2.7/COPYING +0 -674
- plesty_lib-0.2.7/LICENSES/GPL-3.0-or-later.txt +0 -674
- plesty_lib-0.2.7/docs/api_reference.md +0 -16
- plesty_lib-0.2.7/docs/contributing.md +0 -0
- plesty_lib-0.2.7/docs/imgs/device_layers.png +0 -0
- plesty_lib-0.2.7/docs/imgs/device_standard.png +0 -0
- plesty_lib-0.2.7/docs/imgs/plesty_framework.png +0 -0
- plesty_lib-0.2.7/docs/index.md +0 -34
- plesty_lib-0.2.7/plesty/lib/device/__init__.py +0 -7
- plesty_lib-0.2.7/plesty/lib/device/composite_device.py +0 -58
- plesty_lib-0.2.7/plesty/lib/test/__init__.py +0 -7
- plesty_lib-0.2.7/spdx.tmpl +0 -4
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/.gitignore +0 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/LICENSE +0 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/LICENSES/LGPL-3.0-or-later.txt +0 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/README.md +0 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/cmd_solver.md +0 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/error_handling.md +0 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/func_system.md +0 -0
- {plesty_lib-0.2.7 → plesty_lib-0.2.9}/docs/device/param_system.md +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.9 — 2026-07-24
|
|
4
|
+
|
|
5
|
+
- update CI component reference to branch `main`.
|
|
6
|
+
- Pin `[tool.plesty] standard = "quantum"` explicitly: core modules always check at quantum regardless of release version, now that the SDK derives the default standard from the version when unset (sdk#19).
|
|
7
|
+
- Documentation overhaul: the landing page now presents the whole library (devices → data → experiments) instead of devices only; new dedicated guides for the experiment framework (`docs/experiment.md` — plans, journaling, resume, `ExperimentPipeline` contract gates) and `CompositeDevice` (`docs/device/composite_device.md` — resilient remote calls); `doc_model()` documented in the base-device guide and `experiment_pipeline` in the test helpers; the three architecture figures are redrawn as SVGs in the PLESTY palette (the synchronized-device figure now includes the telemetry system); fixed the install instructions (`plesty-lib[dev]` is not an extra) and filled the empty contributing page. The generated API reference now documents `plesty.lib` itself (the deprecated `plestylib` shim is excluded via the SDK's new `api-ignore` setting) and sits as the last item of the navigation; several docstrings were fixed for the zero-warning docs build (RST-safe SCPI `*IDN?`/`*OPC?` mentions, resource-tree examples as literal blocks, experiment-pipeline gate summary as a list).
|
|
8
|
+
- Add a structured, render-agnostic device documentation model (sdk#18): `BaseDeviceSyncModel.doc_model()` returns a `DeviceDocModel` with (1) `parameters` grouped by config group (from the parameter system), (2) `functions` — the device's own operations as unified `FuncDoc` objects, combining schema-registered operations (`source="schema"`) and the device's `@expose_to_api` methods (`source="custom"`), and (3) `standard_methods` — the common device API every device shares (`connect`/`write`/`query`/`reset`/…, `source="standard"`). Backed by new `ConfigSystem.parameter_docs()` and `FunctionSystem.function_docs()`/`standard_method_docs()`; `summary()` is unchanged (renderers can migrate to `doc_model()` later). Framework plumbing is kept out of `functions` and surfaced under `standard_methods`.
|
|
9
|
+
- Enrich `doc_model()` method documentation (sdk#18): `_method_doc` now parses Google-style docstring `Args:`/`Returns:` sections so per-parameter and return descriptions populate `FuncDoc` (previously empty for `@expose_to_api` and standard methods), and `*args`/`**kwargs` are excluded from the documented parameter list. `BaseDeviceSyncModel.write`/`query` gained proper `Args:`/`Returns:` docstrings so the standard methods document their `key`/`value` parameters.
|
|
10
|
+
- Acquire the per-device resource lock on connect, not construction (sdk#18): `ResourceRegistry.acquire()` moved from `BaseDeviceSyncModel.__init__` to `__enter__` (released in `__exit__`, and on a failed connect). Constructing a device is now a side-effect-free operation — two instances of the same device id can coexist and only conflict when both connect — which lets documentation generators and offline tooling instantiate a device without holding hardware locks. `ResourceRegistry.acquire()` now also reclaims a key whose previous holder was garbage-collected (a dead weak reference no longer counts as in-use).
|
|
11
|
+
|
|
12
|
+
- `CompositeDevice` gains resilient remote calls (lib#15): `call(dev, func, ...)` invokes a sub-device method and, on a `TimeoutError`, rebuilds that client (flushing the stale ZMQ reply left in the socket) and retries before the final timeout propagates. A new `reconnect(dev)` and the module-level `connect_client(address, ...)` (build-with-connection-handshake-retry) back it; `__init__` accepts an optional `addresses` mapping so remote sub-devices are reconnectable. Every multi-device composite (e.g. hub scanners) gets this for free instead of re-implementing it.
|
|
13
|
+
- `FunctionSystem` now logs every registered-operation execution at INFO level, symmetric with the `ConfigSystem` write log: `[<device-id>] Running operation <name>(<param>=<value>, ...)`. Parameter values are rendered with a size-bounded repr so array-valued arguments do not flood the log.
|
|
14
|
+
- Add `plesty.lib.device.telemetry` — the unified operational-telemetry schema (lib#4): `DeviceStatus` (pull-style point-in-time snapshot) and `TelemetryEvent` (push-style timestamped record of kind `reading`/`state_change`/`error`/`warning`), plus crash-tolerant JSONL persistence helpers (`append_events`/`read_events`, same format as the experiment journal). The `TelemetrySystem` mixin composes into `BaseDeviceSyncModel` alongside `ConfigSystem`/`FunctionSystem`: every device gets consumer-registered event hooks, `emit_telemetry()`, and a free `status()` snapshot assembled from the connection check and cached parameter values (no device I/O); `write`/`query` emit `state_change`/`reading` events automatically when a hook is registered. No polling loops, threads, or transports — consumers own the event lifecycle.
|
|
15
|
+
- Formalise the experimental-output metadata schema (lib#4): the JSON document written by `save_result` is now the typed `ResultDocument` model (`plesty.lib.data.io`) with validated result types and an automatic write-time stamp; the on-disk layout is unchanged. New `load_document()` reads a result's metadata without any blob I/O; `load_result` routes through the model.
|
|
16
|
+
- Document all three data/metadata schema domains (device configuration, operational telemetry, experimental output) in `docs/data_schemas.md`.
|
|
17
|
+
|
|
18
|
+
## 0.2.8 — 2026-07-07
|
|
19
|
+
|
|
20
|
+
- Add `plesty.lib.test.experiment_pipeline.ExperimentPipeline` (lib#14) — the experiment-tier counterpart of `DevicePipeline`: five hardware-free contract gates (public `Experiment` subclass instantiable without devices; deterministic `build_plan()` by content hash; every `Step.op` resolves via the framework's own plan validation; `Plan.save`/`load` round-trip preserves the hash; `setup`/`teardown` overrides are async) plus a one-shot `run_mock_pipeline()`. Experiment HUB modules expose one pytest function per gate; SDK gate E1 (plesty-sdk#15) verifies their presence and passage, mirroring gate d1.
|
|
21
|
+
|
|
22
|
+
- `Experiment.run` now validates the plan before anything executes: every `Step.op` must name an existing public callable on the experiment and may not be a reserved lifecycle method (`build_plan`, `run`, `setup`, `teardown`) — violations raise the new `InvalidOperationError` listing all offending steps, before any device is touched or journal written. The class docstring now spells out the override contract (`build_plan` required; `setup`/`teardown` optional extensions).
|
|
23
|
+
|
|
24
|
+
- Add `plesty.lib.experiment` — async `Experiment` ABC with atomic scheduling, status journaling and checkpoint/resume (first step toward lib#3): `Plan`/`Step` define the frozen measurement schedule (content-hashed so a resumed run refuses a changed schedule); `Journal` is a crash-safe append-only JSONL status log replayed to derive run state and the completed-step set; `Experiment` provides the async `setup()`/`run()`/`teardown()` lifecycle with per-step retries, clean handling of cancel/error, and resume via `run(resume=<run_id>)` that skips journaled-complete steps.
|
|
25
|
+
- Add `plesty.lib.data.io` (first step toward lib#4): `save_result`/`load_result` persist measurement results crash-safely — blobs stay in their original raw format (`.npy` for `PlestyArray`, native encoding for image bytes) described by a JSON metadata document (Plesty meta, provenance, timestamp) with a reference link to the blob; `convert_to_hdf5` optionally packs such documents into a unified HDF5 archive (requires the optional `h5py` dependency).
|
|
26
|
+
|
|
27
|
+
- Surface `op_schema`-registered functions through the `describe` endpoint (lib#13): `DeviceTCPIPServer._describe` now merges the `FunctionSystem._functions` registry into the method listing. These ops are installed as instance attributes and were therefore invisible to the MRO-based `@expose_to_api` walk, so remote clients could not discover or bind them (though they were already callable via the generic `call` path). `DeviceTCPIPClient` now binds them as local proxy methods.
|
|
28
|
+
- Migrate licensing to the [REUSE](https://reuse.software/) standard (lib#12, follows plesty-sdk#10): Python sources carry inline SPDX headers with a collective `SPDX-FileCopyrightText` (Plesty Development Team) plus a `SPDX-FileContributor` per author; non-code files are covered by a central `REUSE.toml`. The root `LICENSE` is kept for forge (GitLab) license detection; `COPYING`, `spdx.tmpl` and the unused `GPL-3.0-or-later.txt` were removed. `plesty check` Gate 9 now verifies `reuse lint` and authorship currency.
|
|
29
|
+
|
|
30
|
+
## 0.2.7 — 2026-07-02
|
|
31
|
+
|
|
32
|
+
- Add `EnvSettings` in `plesty.lib.utils.settings`: a dependency-free helper that loads environment variables (optionally seeded from a `.env` file, process env taking precedence) with typed `get`/`require` access. By default `load()` also registers parsed `.env` values into `os.environ` without overriding existing ones (dotenv-style; pass `export=False` to disable). Generic by design — device modules choose their own variable names and defaults for credential/connection info (lib#11, companion to plesty-sdk#13)
|
|
33
|
+
- Expose `identity`, `check_errors`, `check_operatability`, `reset`, `clear` to the remote API via `@expose_to_api` on `BaseDeviceSyncModel`
|
|
34
|
+
- Fix `TCPIPTrafficManager._send_command`: pass actual `timeout` to `receive_one` instead of the boolean flag `timeout_overrided` (lib#9)
|
|
35
|
+
- Fix `sock_smart_read` JSON framing: exit the read loop when `terminator in msg` (consistent with line-data branch) and return `msg.rstrip()` — the previous `rstrip(terminator)` only stripped `\n`, leaving `\r` and causing the brace check to fail for `\r\n`-terminated responses (e.g. AMC300) (lib#9)
|
|
36
|
+
- Fix `DeviceTCPIPClient._send`: drain stale ZMQ reply after `recv()` timeout (100 ms window) so the client remains usable on the next call (lib#10)
|
|
37
|
+
|
|
38
|
+
## 2026-06-25
|
|
39
|
+
|
|
40
|
+
- Add `FuncMeta` and `SCPISolver.solve_func` for schema-driven operation dispatch
|
|
41
|
+
- Fix mypy type annotations across all public modules
|
|
42
|
+
- Migrate CI pipeline to `plesty-standard-ci@exp`
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plesty-lib
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: A library for the Plesty ecosystem.
|
|
5
|
+
Author: Plesty Development Team
|
|
5
6
|
Author-email: Yunshuang Yuan <yunshuang.yuan@fkp.uni-hannover.de>
|
|
6
7
|
Maintainer-email: Plesty Development Team <yunshuang.yuan@fkp.uni-hannover.de>
|
|
7
8
|
License-Expression: LGPL-3.0-or-later
|
|
8
|
-
License-File: COPYING
|
|
9
9
|
License-File: LICENSE
|
|
10
|
+
License-File: LICENSES/LGPL-3.0-or-later.txt
|
|
10
11
|
Requires-Python: >=3.12
|
|
11
12
|
Requires-Dist: numpy>=1.24
|
|
12
13
|
Requires-Dist: pyserial>=3.5
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
|
|
3
|
+
# Non-code files (docs, images, configs, the root LICENSE, lockfiles) are
|
|
4
|
+
# licensed here; Python sources carry their own inline SPDX headers. Copyright
|
|
5
|
+
# is held collectively by the Plesty Development Team, with contributors listed
|
|
6
|
+
# below. `reuse lint` (Gate 9 of `plesty check`) verifies full coverage.
|
|
7
|
+
|
|
8
|
+
[[annotations]]
|
|
9
|
+
path = [
|
|
10
|
+
"LICENSE",
|
|
11
|
+
"README.md",
|
|
12
|
+
"CHANGELOG.md",
|
|
13
|
+
"pyproject.toml",
|
|
14
|
+
"uv.lock",
|
|
15
|
+
".gitignore",
|
|
16
|
+
".gitlab-ci.yml",
|
|
17
|
+
"docs/**",
|
|
18
|
+
]
|
|
19
|
+
SPDX-FileCopyrightText = "2026 Plesty Development Team"
|
|
20
|
+
SPDX-FileContributor = "Yunshuang Yuan"
|
|
21
|
+
SPDX-License-Identifier = "LGPL-3.0-or-later"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
Entry page for the [full `plesty.lib` API](reference/plesty/lib/index) documentation.
|
|
3
|
+
|
|
4
|
+
## Core Packages
|
|
5
|
+
|
|
6
|
+
All packages live under the `plesty.lib` namespace (import as
|
|
7
|
+
`plesty.lib.<package>`).
|
|
8
|
+
|
|
9
|
+
| Package | Description |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| [`data`](reference/plesty/lib/data/index) | Data containers, units, type helpers, and result persistence
|
|
12
|
+
| [`device`](reference/plesty/lib/device/index) | Base device model, parameter/function/telemetry systems, async wrappers, composite devices |
|
|
13
|
+
| [`experiment`](reference/plesty/lib/experiment/index) | Async experiment lifecycle: `Experiment` ABC, `Plan`/`Step` schedules, crash-safe `Journal`, checkpoint/resume |
|
|
14
|
+
| [`traffic`](reference/plesty/lib/traffic/index) | Transport backends (TCP/IP, VISA, Serial) |
|
|
15
|
+
| [`solver`](reference/plesty/lib/solver/index) | Command and operation solvers |
|
|
16
|
+
| [`service`](reference/plesty/lib/service/index) | TCP/IP server/client and service builders |
|
|
17
|
+
| [`sim`](reference/plesty/lib/sim/index) | Synthetic data generation for testing |
|
|
18
|
+
| [`utils`](reference/plesty/lib/utils/index) | Shared utilities (env settings loader, logging, errors, registry, DLL helpers) |
|
|
19
|
+
| [`test`](reference/plesty/lib/test/index) | Test helpers |
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
PlestyLib is developed under the PLESTY governance standard. If you want to
|
|
4
|
+
contribute — a fix, a feature, or a whole new HUB module built on the
|
|
5
|
+
library — start with the
|
|
6
|
+
[PLESTY Contributor Guide](https://docs.plesty.net/contributor-guide/), which
|
|
7
|
+
covers the full workflow end to end.
|
|
8
|
+
|
|
9
|
+
The short version:
|
|
10
|
+
|
|
11
|
+
1. **Branching** — work never lands on `main` directly. Branch from the
|
|
12
|
+
up-to-date `exp` branch (`git checkout exp && git pull && git checkout -b
|
|
13
|
+
<type>/<slug>`), implement, and merge back into `exp`.
|
|
14
|
+
2. **Quality gates** — every change must pass `uv run plesty check` (the
|
|
15
|
+
`quantum` standard for core modules: formatting, typing, docstrings,
|
|
16
|
+
tests, licensing, and more). The pre-push hook installed by `plesty init`
|
|
17
|
+
runs it automatically.
|
|
18
|
+
3. **Changelog** — add an entry under `## Unreleased` in `CHANGELOG.md` in
|
|
19
|
+
the same commit as your change.
|
|
20
|
+
4. **Licensing** — the project follows the [REUSE](https://reuse.software/)
|
|
21
|
+
standard: Python sources carry SPDX headers with a
|
|
22
|
+
`SPDX-FileContributor` line per author; add yourself when you touch a
|
|
23
|
+
file.
|
|
24
|
+
|
|
25
|
+
Issues and merge requests live on
|
|
26
|
+
[gitlab.com/plesty/core/plesty-lib](https://gitlab.com/plesty/core/plesty-lib).
|
|
27
|
+
For questions about the module ecosystem, browse the searchable
|
|
28
|
+
[module registry](https://hub.plesty.net).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Plesty Development Team
|
|
3
|
+
SPDX-FileContributor: Yunshuang Yuan
|
|
4
|
+
|
|
5
|
+
SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Data & Metadata Schemas
|
|
9
|
+
|
|
10
|
+
`plesty-lib` defines one schema contract per data domain so that every HUB
|
|
11
|
+
module acquires, stores, and exchanges data in the same shape. Three domains
|
|
12
|
+
are covered (issue lib#4):
|
|
13
|
+
|
|
14
|
+
| Domain | Schema | Module |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Device configuration | `ConfigParameter` / `ConfigGroup` | `plesty.lib.device.params` |
|
|
17
|
+
| Operational telemetry | `DeviceStatus` / `TelemetryEvent` | `plesty.lib.device.telemetry` |
|
|
18
|
+
| Experimental output | `ResultDocument` | `plesty.lib.data.io` |
|
|
19
|
+
|
|
20
|
+
## Device configuration
|
|
21
|
+
|
|
22
|
+
Structured representation of device parameters and settings — see
|
|
23
|
+
[Parameter System](device/param_system.md). Every parameter is a
|
|
24
|
+
`ConfigParameter` (name, dtype, unit, constraints, command binding) organised
|
|
25
|
+
into `ConfigGroup`s and managed by the `ConfigSystem` that every device model
|
|
26
|
+
composes. Device modules declare their parameters in a YAML/JSON schema which
|
|
27
|
+
`ConfigSystem` materialises at init.
|
|
28
|
+
|
|
29
|
+
## Operational telemetry
|
|
30
|
+
|
|
31
|
+
Real-time status, state, and diagnostic data, defined in
|
|
32
|
+
`plesty.lib.device.telemetry`. Two shapes cover pull and push:
|
|
33
|
+
|
|
34
|
+
* **`DeviceStatus`** — a point-in-time snapshot. Every device inherits
|
|
35
|
+
`status()` via the `TelemetrySystem` mixin; the default implementation is
|
|
36
|
+
assembled from the connection check and the cached configuration values
|
|
37
|
+
(no device I/O), so it is always cheap to call:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
status = device.status()
|
|
41
|
+
status.connection # "connected" | "disconnected" | "connecting" | "error"
|
|
42
|
+
status.state # "ready", "busy", ... (override device_state() to refine)
|
|
43
|
+
status.parameters # last-known parameter values
|
|
44
|
+
status.last_error # most recent error message, if any
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
* **`TelemetryEvent`** — a timestamped record of kind `reading`,
|
|
48
|
+
`state_change`, `error`, or `warning`. Consumers register a hook; the base
|
|
49
|
+
device emits `state_change`/`reading` events automatically on every
|
|
50
|
+
successful `write`/`query`, and devices can emit custom events with
|
|
51
|
+
`emit_telemetry()`:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
events = []
|
|
55
|
+
device.register_telemetry_hook(events.append)
|
|
56
|
+
device.write("wavelength", 850.0) # emits a state_change event
|
|
57
|
+
|
|
58
|
+
from plesty.lib.device.telemetry import append_events
|
|
59
|
+
append_events(run_dir / "telemetry.jsonl", events)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Hooks run synchronously and never disturb device operation (a failing hook is
|
|
63
|
+
logged and skipped). When no hook is registered, no event is constructed.
|
|
64
|
+
`append_events`/`read_events` persist events in the same crash-tolerant
|
|
65
|
+
append-only JSONL format as the experiment journal. Polling loops, threads,
|
|
66
|
+
and transports are deliberately out of scope — they belong to monitors and
|
|
67
|
+
services, not to the schema.
|
|
68
|
+
|
|
69
|
+
## Experimental output
|
|
70
|
+
|
|
71
|
+
Acquired data plus its metadata, defined in `plesty.lib.data.io`. The storage
|
|
72
|
+
contract keeps blobs in their **original raw format** (`.npy` for
|
|
73
|
+
`PlestyArray`, native encoding for image bytes) described by a JSON metadata
|
|
74
|
+
document — the typed `ResultDocument` model — holding the Plesty meta (name,
|
|
75
|
+
unit, range, description), timestamps, provenance, and a reference link to
|
|
76
|
+
the blob:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from plesty.lib.data import save_result, load_result, load_document
|
|
80
|
+
|
|
81
|
+
path = save_result(array, run_dir / "step_0001", provenance={"step_id": "s1"})
|
|
82
|
+
document = load_document(path) # metadata only — no blob I/O
|
|
83
|
+
result = load_result(path) # reconstructed PlestyArray
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The JSON document is the commit record: a result exists once its `.json`
|
|
87
|
+
file does, and writes are crash-safe (blob first, document last, both
|
|
88
|
+
atomic). `convert_to_hdf5` optionally packs a set of documents into a single
|
|
89
|
+
HDF5 archive for exchange (requires the optional `h5py` dependency).
|
|
@@ -19,7 +19,7 @@ When your device class inherits from `BaseDeviceSyncModel`, you get:
|
|
|
19
19
|
3. Built-in high-level methods such as `write`, `query`, `state`, and `summary`.
|
|
20
20
|
4. Context manager support (`with device:`) that calls `init`, `connect`, and `disconnect`.
|
|
21
21
|
5. Resource tracking through `ResourceRegistry` to avoid duplicate ownership.
|
|
22
|
-
6. Per-
|
|
22
|
+
6. Per-device resource management (`register_resources`, `allocate_resource`, …) for multi-client access control.
|
|
23
23
|
|
|
24
24
|
## Typical Layering
|
|
25
25
|
|
|
@@ -128,7 +128,8 @@ Useful built-in helpers:
|
|
|
128
128
|
|
|
129
129
|
1. `state` / `get_state()`: query all registered parameters and return a dictionary.
|
|
130
130
|
2. `synchronize_param_from_device(keys=None, sync_constraints=False)`: pull current values from hardware into the model.
|
|
131
|
-
3. `summary()`:
|
|
131
|
+
3. `summary(style=...)`: render a textual API overview (`short`, `md`, or `google`).
|
|
132
|
+
4. `doc_model()`: return the structured documentation model (see below).
|
|
132
133
|
|
|
133
134
|
If your device supports querying dynamic constraints, implement:
|
|
134
135
|
|
|
@@ -160,33 +161,64 @@ asyncio.run(main())
|
|
|
160
161
|
|
|
161
162
|
## Multi-Client Resource Control
|
|
162
163
|
|
|
163
|
-
`BaseDeviceSyncModel`
|
|
164
|
+
`BaseDeviceSyncModel` embeds resource tracking that supports exclusive access
|
|
164
165
|
control when a device is shared by multiple clients (for example, over a TCP/IP service):
|
|
165
166
|
|
|
166
|
-
|
|
|
167
|
+
| Member | Type | Purpose |
|
|
167
168
|
|---|---|---|
|
|
168
|
-
| `_resources` | `list` |
|
|
169
|
-
| `
|
|
170
|
-
| `_clients` | `dict` | Maps `client_id → [allocated resources]` |
|
|
169
|
+
| `_resources` | `list` | Inventory of physical resources this device owns; passed to the TCP server for per-client access control |
|
|
170
|
+
| `_resource_manager` | `ResourceManager \| None` | Embedded manager for direct (non-TCP) clients, created lazily by `register_resources()` |
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
Four public methods delegate to the embedded `ResourceManager`
|
|
173
|
+
(`plesty.lib.service.resource_manager`):
|
|
174
|
+
|
|
175
|
+
1. `register_resources(resources)`: declare the resources the device owns (flat list, set, or nested dict tree).
|
|
176
|
+
2. `allocate_resource(resources, client_id)`: request exclusive access; returns `False` if any requested resource is already held by another client.
|
|
177
|
+
3. `release_resource(client_id)`: free every resource held by that client.
|
|
178
|
+
4. `client_resources(client_id)`: canonical resource paths currently allocated to that client.
|
|
175
179
|
|
|
176
180
|
The default `_user_permission_check` always returns `True` (no restriction). Override it to
|
|
177
|
-
implement real guard logic
|
|
181
|
+
implement real guard logic — query current allocations via `client_resources` or the embedded
|
|
182
|
+
manager:
|
|
178
183
|
|
|
179
184
|
```python
|
|
180
185
|
def _user_permission_check(self, user_resources: list, key: str) -> bool:
|
|
181
|
-
if not user_resources:
|
|
186
|
+
if not user_resources or self._resource_manager is None:
|
|
182
187
|
return True
|
|
183
|
-
|
|
188
|
+
locked = self._resource_manager._locked_resources
|
|
189
|
+
return any(r in locked for r in user_resources)
|
|
184
190
|
```
|
|
185
191
|
|
|
186
192
|
Both `write` and `query` pass a `resources` kwarg through to `_user_permission_check` when the
|
|
187
193
|
caller supplies it, so access control integrates transparently with the standard operation
|
|
188
194
|
interface.
|
|
189
195
|
|
|
196
|
+
## Documentation Model (`doc_model`)
|
|
197
|
+
|
|
198
|
+
Every device exposes a structured, render-agnostic documentation model —
|
|
199
|
+
the single source of truth for its user-facing documentation:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
model = device.doc_model()
|
|
203
|
+
model.parameters # {group_name: [ConfigParameter, ...]} from the parameter system
|
|
204
|
+
model.functions # the device's own operations as unified FuncDoc objects
|
|
205
|
+
model.standard_methods # the common device API (connect/write/query/reset/...)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`functions` unifies the two ways a device gains operations: schema-registered
|
|
209
|
+
operations (`source="schema"`) and `@expose_to_api` methods
|
|
210
|
+
(`source="custom"`); framework plumbing is kept out and surfaced separately
|
|
211
|
+
under `standard_methods` (`source="standard"`). Per-parameter and return
|
|
212
|
+
descriptions are parsed from Google-style docstring `Args:`/`Returns:`
|
|
213
|
+
sections — write those in your device methods and the generated documentation
|
|
214
|
+
inherits them.
|
|
215
|
+
|
|
216
|
+
Renderers consume the model instead of re-deriving metadata: the SDK docs
|
|
217
|
+
generator, `summary()`, and the server `describe` endpoint all build on it.
|
|
218
|
+
Because constructing a device is side-effect-free (the per-device resource
|
|
219
|
+
lock is only acquired on `connect`), documentation tooling can instantiate
|
|
220
|
+
your device without touching hardware.
|
|
221
|
+
|
|
190
222
|
## Best Practices
|
|
191
223
|
|
|
192
224
|
1. Keep transport logic in traffic managers, not in device classes.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Composite Device
|
|
2
|
+
|
|
3
|
+
`CompositeDevice` groups several devices — local instances and/or remote
|
|
4
|
+
TCP/IP clients — behind one object, so an orchestration (typically an
|
|
5
|
+
[Experiment](../experiment.md)) manages a whole setup as a single unit.
|
|
6
|
+
|
|
7
|
+
## Building a composite
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from plesty.lib.device.composite_device import CompositeDevice, connect_client
|
|
11
|
+
|
|
12
|
+
composite = CompositeDevice(
|
|
13
|
+
devices={
|
|
14
|
+
"stage": stage_device, # local BaseDeviceSyncModel
|
|
15
|
+
"cam": connect_client("tcp://192.168.1.20:5555"), # remote client
|
|
16
|
+
},
|
|
17
|
+
addresses={"cam": "tcp://192.168.1.20:5555"}, # remote devices that
|
|
18
|
+
) # should be reconnectable
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Sub-devices are reached by name (`composite.stage`, `composite.cam`).
|
|
22
|
+
`connect_all()` / `disconnect_all()` handle every local sub-device, and the
|
|
23
|
+
composite is a context manager:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
with composite:
|
|
27
|
+
print(composite.identity()) # {"stage": ..., "cam": ...}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Remote clients manage their own connection; the `addresses` mapping is only
|
|
31
|
+
needed for remote sub-devices that should be *reconnectable* (below).
|
|
32
|
+
|
|
33
|
+
## Resilient remote calls
|
|
34
|
+
|
|
35
|
+
Long-running orchestrations must survive transient network hiccups. Instead
|
|
36
|
+
of calling a remote sub-device method directly, route it through `call()`:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
frame = composite.call("cam", "acquire", exposure_s=0.1)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
On a `TimeoutError`, `call()` rebuilds the sub-device's client — discarding
|
|
43
|
+
the stale ZMQ reply left in the socket, which would otherwise poison every
|
|
44
|
+
subsequent request — waits briefly, and retries (default 3 attempts) before
|
|
45
|
+
the final timeout propagates. `reconnect(dev)` performs the same rebuild
|
|
46
|
+
explicitly.
|
|
47
|
+
|
|
48
|
+
`connect_client(address)` is the module-level builder used for the rebuilds:
|
|
49
|
+
`build_client` plus a connection-handshake retry with linear backoff, which
|
|
50
|
+
matters when several clients open back-to-back against the same server.
|
|
51
|
+
|
|
52
|
+
Every multi-device composite gets this robustness for free instead of
|
|
53
|
+
re-implementing it per module.
|
|
54
|
+
|
|
55
|
+
## Interplay with experiments
|
|
56
|
+
|
|
57
|
+
`Experiment` accepts a `CompositeDevice` at construction; its default
|
|
58
|
+
`setup()`/`teardown()` call `connect_all()`/`disconnect_all()`. Step
|
|
59
|
+
operations then drive sub-devices via `self.devices.<name>` or, for remote
|
|
60
|
+
calls that should retry, `self.devices.call(...)`.
|
|
@@ -11,6 +11,10 @@ If you are building a new device API, read in this order:
|
|
|
11
11
|
7. [Logging System](logging_system.md)
|
|
12
12
|
8. [Test Helpers](test_helper.md)
|
|
13
13
|
|
|
14
|
+
To orchestrate several devices as one unit (the shape an
|
|
15
|
+
[Experiment](../experiment.md) consumes), see
|
|
16
|
+
[Composite Device](composite_device.md).
|
|
17
|
+
|
|
14
18
|
## Topic Guide
|
|
15
19
|
|
|
16
20
|
### Core Architecture
|
|
@@ -18,6 +22,7 @@ If you are building a new device API, read in this order:
|
|
|
18
22
|
1. [Base Device](base_device.md): Defines the synchronized device abstraction and lifecycle.
|
|
19
23
|
2. [Traffic Manager](traffic_manager.md): Implements transport communication (Serial, VISA, TCP/IP).
|
|
20
24
|
3. [Command Solver](cmd_solver.md): Maps standardized operations to protocol commands.
|
|
25
|
+
4. [Composite Device](composite_device.md): Groups local and remote devices with resilient remote calls.
|
|
21
26
|
|
|
22
27
|
### Metadata and API Contracts
|
|
23
28
|
|
|
@@ -80,18 +80,17 @@ from plesty.lib.device.base_tcp_scpi_device import BaseTCPScpiDevice
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
class DemoScpiDevice(BaseTCPScpiDevice):
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
def __init__(self, host: str, port: int):
|
|
84
|
+
super().__init__(host=host, port=port)
|
|
85
|
+
self.register_config("POWER", dtype=float, read_only=True, command="MEAS:POW")
|
|
86
86
|
logging.info("Initialization finished.")
|
|
87
|
-
|
|
88
|
-
def some_utility_function():
|
|
87
|
+
|
|
88
|
+
def some_utility_function(self) -> None:
|
|
89
89
|
try:
|
|
90
90
|
# do something
|
|
91
91
|
logging.debug("xxx done")
|
|
92
92
|
except Exception as e:
|
|
93
93
|
logging.error(f"Error: {e}")
|
|
94
|
-
|
|
95
94
|
```
|
|
96
95
|
|
|
97
96
|
Why this helps developers:
|
|
@@ -13,6 +13,8 @@ and surface obvious breakage quickly.
|
|
|
13
13
|
| `grouped_param_keys` | pytest fixtures that verify grouped parameter key resolution |
|
|
14
14
|
| `resource_allocation` | Multiprocessing helpers for testing multi-client resource contention |
|
|
15
15
|
| `schema_params` | Helpers for refreshing a JSON parameter schema from a real device |
|
|
16
|
+
| `device_pipeline` | Device-tier contract gates (SDK gate d1) |
|
|
17
|
+
| `experiment_pipeline` | Experiment-tier contract gates (SDK gate E1), hardware-free |
|
|
16
18
|
|
|
17
19
|
---
|
|
18
20
|
|
|
@@ -285,6 +287,93 @@ The function is marked `__test__ = False` so pytest does not auto-collect it.
|
|
|
285
287
|
|
|
286
288
|
---
|
|
287
289
|
|
|
290
|
+
## 6. `device_pipeline` — Device Contract Gates
|
|
291
|
+
|
|
292
|
+
**Location:** `plesty.lib.test.device_pipeline`
|
|
293
|
+
|
|
294
|
+
The standard test pipeline for a PLESTY device API module (SDK gate d1). Construct a
|
|
295
|
+
`DevicePipeline` with the device class, its constructor arguments, and the schema paths,
|
|
296
|
+
then call the individual gate methods from pytest test functions — or `run_mock_pipeline()`
|
|
297
|
+
to run all mock gates in one shot.
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
# tests/test_pipeline.py of a device HUB module
|
|
301
|
+
from plesty.lib.test.device_pipeline import DevicePipeline
|
|
302
|
+
|
|
303
|
+
PIPELINE = DevicePipeline(
|
|
304
|
+
MyDevice,
|
|
305
|
+
"device-id",
|
|
306
|
+
param_schema="assets/param_schema.json",
|
|
307
|
+
op_schema="assets/op_schema.json",
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def test_schema_integrity():
|
|
311
|
+
PIPELINE.test_schema_integrity()
|
|
312
|
+
|
|
313
|
+
def test_params_mock():
|
|
314
|
+
PIPELINE.test_params_mock()
|
|
315
|
+
|
|
316
|
+
# ... one pytest function per gate
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
| Gate | Method | Checks |
|
|
320
|
+
|---|---|---|
|
|
321
|
+
| 1 | `test_schema_integrity` | `param_schema.json` / `op_schema.json` are well-formed (valid JSON, no duplicate keys, valid types) |
|
|
322
|
+
| 2 | `test_param_key_resolution` | every key in `get_config_list()` resolves via `get_config(key)` |
|
|
323
|
+
| 3 | `test_params_mock` | all config params round-trip through the mock solver |
|
|
324
|
+
| 4 | `test_funcs_mock` | all registered operations accept generated payloads |
|
|
325
|
+
| 5 | `test_lifecycle` | context manager completes; `is_operatable` is `True` after connect |
|
|
326
|
+
| 6 | `test_identity` | `identity()` returns a non-empty string |
|
|
327
|
+
| 7 | `test_check_errors` | `check_errors()` returns an empty list on a healthy mock device |
|
|
328
|
+
| 8 | `test_state_coverage` | `state` keys are a superset of `get_config_list()` |
|
|
329
|
+
| 9 | `test_resource_allocation` | multi-client resource contention is enforced (optional, spawns server + 3 clients) |
|
|
330
|
+
| 10 | `test_hardware_schema_refresh` | real hardware refreshes schema defaults/types (`@pytest.mark.hardware`, skipped in CI) |
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 7. `experiment_pipeline` — Experiment Contract Gates
|
|
335
|
+
|
|
336
|
+
The experiment-tier counterpart of `DevicePipeline`: five hardware-free gates
|
|
337
|
+
validating the plan/checkpoint contract of an
|
|
338
|
+
[Experiment](../experiment.md) subclass. The pipeline never calls `run()`,
|
|
339
|
+
so no instrument is needed.
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
# tests/test_pipeline.py of an experiment HUB module
|
|
343
|
+
from plesty.lib.test.experiment_pipeline import ExperimentPipeline
|
|
344
|
+
|
|
345
|
+
PIPELINE = ExperimentPipeline(MyExperiment)
|
|
346
|
+
|
|
347
|
+
def test_experiment_subclass():
|
|
348
|
+
PIPELINE.test_experiment_subclass()
|
|
349
|
+
|
|
350
|
+
def test_plan_deterministic():
|
|
351
|
+
PIPELINE.test_plan_deterministic()
|
|
352
|
+
|
|
353
|
+
def test_plan_ops_resolve():
|
|
354
|
+
PIPELINE.test_plan_ops_resolve()
|
|
355
|
+
|
|
356
|
+
def test_plan_serializable():
|
|
357
|
+
PIPELINE.test_plan_serializable()
|
|
358
|
+
|
|
359
|
+
def test_lifecycle_hooks():
|
|
360
|
+
PIPELINE.test_lifecycle_hooks()
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
| Gate | Checks |
|
|
364
|
+
|---|---|
|
|
365
|
+
| 1 `test_experiment_subclass` | public `Experiment` subclass, instantiable without devices |
|
|
366
|
+
| 2 `test_plan_deterministic` | `build_plan()` twice yields the same `content_hash()` |
|
|
367
|
+
| 3 `test_plan_ops_resolve` | every `Step.op` resolves via the framework's plan validation |
|
|
368
|
+
| 4 `test_plan_serializable` | `Plan.save`/`load` round-trip preserves the hash |
|
|
369
|
+
| 5 `test_lifecycle_hooks` | `setup`/`teardown` overrides are async |
|
|
370
|
+
|
|
371
|
+
`run_mock_pipeline()` runs all gates in one shot. Experiment HUB modules
|
|
372
|
+
expose one pytest function per gate — SDK gate E1 verifies their presence
|
|
373
|
+
and passage, mirroring the device-tier gate d1.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
288
377
|
## Recommended Workflow
|
|
289
378
|
|
|
290
379
|
1. Start with `device_param_system.auto_test(use_mock_solver=True)` after registering parameters —
|
|
@@ -42,7 +42,7 @@ Common built-in traffic managers include:
|
|
|
42
42
|
|
|
43
43
|
1. `SerialTrafficManager` for serial ports.
|
|
44
44
|
2. `VisaTrafficManager` for VISA resources.
|
|
45
|
-
3. `
|
|
45
|
+
3. `TCPIPTrafficManager` for TCP/IP connections.
|
|
46
46
|
|
|
47
47
|
## Demo 1: Use the Built-in SerialTrafficManager
|
|
48
48
|
|