plesty-lib 0.3.5.dev1__tar.gz → 0.3.5.dev2__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.
Files changed (161) hide show
  1. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/CHANGELOG.md +61 -0
  2. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/PKG-INFO +1 -1
  3. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/composite_device.md +51 -1
  4. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/experiment.md +47 -0
  5. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/gl-secret-detection-report.json +5 -5
  6. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/composite_device.py +400 -26
  7. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/experiment/__init__.py +3 -0
  8. plesty_lib-0.3.5.dev2/plesty/lib/experiment/assets/preflight.yaml +20 -0
  9. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/experiment/base_experiment.py +43 -3
  10. plesty_lib-0.3.5.dev2/plesty/lib/experiment/preflight.py +168 -0
  11. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/service/tcp_ip_client.py +25 -4
  12. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/utils/logger.py +54 -20
  13. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_composite_config.py +280 -4
  14. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_device_base.py +4 -0
  15. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_experiment.py +30 -0
  16. plesty_lib-0.3.5.dev2/tests/test_experiment_preflight.py +114 -0
  17. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_utils.py +36 -0
  18. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/.gitignore +0 -0
  19. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/.gitlab-ci.yml +0 -0
  20. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/LICENSE +0 -0
  21. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/LICENSES/LGPL-3.0-or-later.txt +0 -0
  22. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/README.md +0 -0
  23. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/REUSE.toml +0 -0
  24. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/analyzer.md +0 -0
  25. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/api_reference.md +0 -0
  26. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/architecture.md +0 -0
  27. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/contributing.md +0 -0
  28. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/data_schemas.md +0 -0
  29. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/base_device.md +0 -0
  30. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/cmd_solver.md +0 -0
  31. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/error_handling.md +0 -0
  32. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/func_system.md +0 -0
  33. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/index.md +0 -0
  34. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/logging_system.md +0 -0
  35. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/param_system.md +0 -0
  36. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/test_helper.md +0 -0
  37. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/device/traffic_manager.md +0 -0
  38. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/imgs/device_layers.svg +0 -0
  39. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/imgs/device_standard.svg +0 -0
  40. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/imgs/plesty_framework.svg +0 -0
  41. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/index.md +0 -0
  42. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/monitor.md +0 -0
  43. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/quickstart.md +0 -0
  44. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/toc.yaml +0 -0
  45. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/docs/ui.md +0 -0
  46. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/__init__.py +0 -0
  47. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/analyzer/__init__.py +0 -0
  48. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/analyzer/base_analyzer.py +0 -0
  49. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/data/__init__.py +0 -0
  50. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/data/array.py +0 -0
  51. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/data/ctype_manager.py +0 -0
  52. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/data/io.py +0 -0
  53. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/data/table.py +0 -0
  54. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/data/types.py +0 -0
  55. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/data/units.py +0 -0
  56. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/__init__.py +0 -0
  57. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/async_wrapper.py +0 -0
  58. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/base_apt_device.py +0 -0
  59. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/base_device_sync.py +0 -0
  60. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/base_tcp_scpi_device.py +0 -0
  61. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/base_visa_scpi_device.py +0 -0
  62. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/device_utils.py +0 -0
  63. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/doc.py +0 -0
  64. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/funcs.py +0 -0
  65. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/params.py +0 -0
  66. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/device/telemetry.py +0 -0
  67. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/experiment/journal.py +0 -0
  68. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/experiment/runs.py +0 -0
  69. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/experiment/schedule.py +0 -0
  70. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/monitor/__init__.py +0 -0
  71. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/monitor/assets/viz.yaml +0 -0
  72. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/monitor/base_monitor.py +0 -0
  73. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/monitor/sources.py +0 -0
  74. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/monitor/viz.py +0 -0
  75. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/service/__init__.py +0 -0
  76. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/service/resource_manager.py +0 -0
  77. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/service/tcp_ip_server.py +0 -0
  78. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/sim/__init__.py +0 -0
  79. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/sim/apt.py +0 -0
  80. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/sim/data_generator.py +0 -0
  81. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/sim/demo_device.py +0 -0
  82. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/solver/__init__.py +0 -0
  83. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/solver/iceblock.py +0 -0
  84. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/solver/scpi.py +0 -0
  85. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/__init__.py +0 -0
  86. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/analyzer_pipeline.py +0 -0
  87. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/client_field_test.py +0 -0
  88. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/device_func_system.py +0 -0
  89. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/device_param_system.py +0 -0
  90. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/device_pipeline.py +0 -0
  91. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/experiment_pipeline.py +0 -0
  92. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/field_test.py +0 -0
  93. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/field_test_concurrency.py +0 -0
  94. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/grouped_param_keys.py +0 -0
  95. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/report_artifact.py +0 -0
  96. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/resource_allocation.py +0 -0
  97. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/schema_params.py +0 -0
  98. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/test/schema_refresh.py +0 -0
  99. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/traffic/__init__.py +0 -0
  100. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/traffic/apt.py +0 -0
  101. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/traffic/apt_protocol.py +0 -0
  102. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/traffic/serial.py +0 -0
  103. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/traffic/tcp_ip.py +0 -0
  104. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/traffic/usb_utils.py +0 -0
  105. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/traffic/utils.py +0 -0
  106. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/traffic/visa.py +0 -0
  107. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/__init__.py +0 -0
  108. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/assets/palette.json +0 -0
  109. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/assets/shell.qss +0 -0
  110. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/monitor_panel.py +0 -0
  111. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/panel.py +0 -0
  112. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/qt/__init__.py +0 -0
  113. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/qt/app.py +0 -0
  114. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/qt/plot.py +0 -0
  115. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/qt/recorder.py +0 -0
  116. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/qt/shell.py +0 -0
  117. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/ui/theme.py +0 -0
  118. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/utils/__init__.py +0 -0
  119. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/utils/config.py +0 -0
  120. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/utils/dll_utils.py +0 -0
  121. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/utils/error_utils.py +0 -0
  122. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/utils/registry.py +0 -0
  123. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/plesty/lib/utils/settings.py +0 -0
  124. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/pyproject.toml +0 -0
  125. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_analyzer.py +0 -0
  126. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_analyzer_pipeline.py +0 -0
  127. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_async_wrapper_threading.py +0 -0
  128. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_client_field_test.py +0 -0
  129. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_config.py +0 -0
  130. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_data_array.py +0 -0
  131. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_data_io_hdf5.py +0 -0
  132. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_data_types.py +0 -0
  133. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_demo_device.py +0 -0
  134. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_device_apt.py +0 -0
  135. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_device_data_path.py +0 -0
  136. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_device_funcs.py +0 -0
  137. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_device_params.py +0 -0
  138. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_device_pipeline.py +0 -0
  139. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_device_scpi.py +0 -0
  140. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_device_telemetry.py +0 -0
  141. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_experiment_pipeline.py +0 -0
  142. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_experiment_runs.py +0 -0
  143. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_field_test.py +0 -0
  144. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_misc.py +0 -0
  145. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_monitor.py +0 -0
  146. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_report_artifact.py +0 -0
  147. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_schema_params.py +0 -0
  148. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_schema_refresh.py +0 -0
  149. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_service_manager.py +0 -0
  150. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_settings.py +0 -0
  151. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_shipped_imports.py +0 -0
  152. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_sim.py +0 -0
  153. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_solver.py +0 -0
  154. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_tcp_ip_resources.py +0 -0
  155. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_traffic_drivers.py +0 -0
  156. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_traffic_orphan.py +0 -0
  157. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_traffic_utils.py +0 -0
  158. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_ui.py +0 -0
  159. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_visa_discovery.py +0 -0
  160. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/tests/test_viz.py +0 -0
  161. {plesty_lib-0.3.5.dev1 → plesty_lib-0.3.5.dev2}/uv.lock +0 -0
@@ -1,5 +1,66 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.5.dev2 — 2026-08-17
4
+
5
+ Development pre-release for field-testing the rig start-up (#36, #37):
6
+ bounded, visible connects, the preflight, and default logging. Same line as
7
+ 0.3.5.dev1 — the final release is 0.4.0.
8
+
9
+ - **A run starts only on a rig that is really there** (pol-pl field
10
+ finding: a sweep went on with the powermeter server absent).
11
+ `CompositeDevice.preflight()` checks that every remote sub-device exposes
12
+ the methods listed under the new `requires` config key and answers
13
+ `identity()`; `Experiment.setup()` runs it and raises `RigNotReadyError`
14
+ on any problem. `plesty.lib.experiment.connect_rig(build, optional=…)`
15
+ is the start-up for every experiment's `__main__`: build, preflight, and
16
+ on a failure print which device, at which address, from which variable,
17
+ then ask `[r]etry / [a]bort / [s]kip` (skip only for optional
18
+ sub-devices, returned as `skipped`); non-interactive processes get the
19
+ diagnosis logged and `RigNotReadyError`. Text in
20
+ `experiment/assets/preflight.yaml`.
21
+ - **A composite that is given up closes its clients.** New
22
+ `CompositeDevice.release()` closes every remote client on its owning
23
+ thread (server-side disconnect with a 1 s window, linger 0) and stops the
24
+ threads; `disconnect_all()` does the same for remote clients, and a first
25
+ connect that fails closes the clients that did connect. A client left to
26
+ the garbage collector blocked the interpreter in its ZMQ context's
27
+ `__del__` — seen mid-run after `connect_rig` rebuilt a rig without the
28
+ powermeter. `connect_rig` releases a rig it gives up.
29
+ - **A first connect to a dead server now returns.** The client's connect
30
+ failure path closed its DEALER socket with the default infinite LINGER,
31
+ so `ctx.term()` blocked forever on the undelivered handshake — a rig built
32
+ against one absent server hung with no output, and no attempt ladder or
33
+ deadline could bound it. `_DeviceTCPIPClient` now drops undelivered
34
+ messages (linger 0) when a connect fails and on `close()`.
35
+ `CompositeDevice` also resolves every address before starting any connect
36
+ (a half-configured `.env` raises `ValueError` without touching a server)
37
+ and stops its worker threads when a first connect fails.
38
+ - **`CompositeDevice` signals sub-device connection state and bounds the first
39
+ connect** (#36). Each sub-device carries a `ConnectionState`
40
+ (`CONNECTING → CONNECTED → DEGRADED → DISCONNECTED`), read with
41
+ `status()` / `status(dev)` and observed with `on_status_change(callback)`;
42
+ every transition is logged (WARNING for `DEGRADED` / `DISCONNECTED`).
43
+ New config key `connect_deadline_s` (default 10 s): a server that does not
44
+ answer within it raises `DeviceUnreachableError(dev, address, elapsed_s)`
45
+ from the constructor instead of running attempts × timeout × backoff
46
+ (~40 s per dead server before). Under thread affinity the first connects
47
+ run in parallel on the sub-devices' own threads. `reconnect()` reuses the
48
+ sub-device's configured `timeout_ms` / `attempts` / `backoff_s` instead of
49
+ the 50 s defaults; a call timeout marks the sub-device `DEGRADED` before
50
+ the rebuild. `_DeviceTCPIPClient.set_default_timeout(ms)` added.
51
+ - **Default logging for experiments and composites** (#37): nothing is
52
+ silent anymore without module code. `Experiment` logs
53
+ `Run <id>: N step(s) (k already completed), run dir …`, `Setup: …`,
54
+ `Step i/N <id> (<op>)` / `Step i/N done in x s`, `Teardown: …`, and
55
+ `Run … completed (N steps) in x s`. `CompositeDevice` logs
56
+ `Connecting <dev> @ <addr>` / `Connected <dev> @ <addr> in x s`, each failed
57
+ attempt with the retry delay, `Reconnecting <dev> @ <addr>`, and the timeout
58
+ a retried call ran under. New `plesty.lib.utils.logger.ensure_logging()`
59
+ (root level + one console sink, idempotent) is called by both constructors;
60
+ `setup_logging(app)` builds on it and adds the per-process log file — the
61
+ first `app` names the file, so a rig built before its experiment no longer
62
+ steals the file name.
63
+
3
64
  ## 0.3.5.dev1 — 2026-08-17
4
65
 
5
66
  Development pre-release for field-testing the run convention (#34); the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: plesty-lib
3
- Version: 0.3.5.dev1
3
+ Version: 0.3.5.dev2
4
4
  Summary: A library for the Plesty ecosystem.
5
5
  Author: Plesty Development Team
6
6
  Author-email: Yunshuang Yuan <yunshuang.yuan@fkp.uni-hannover.de>
@@ -60,7 +60,9 @@ composite = CompositeDevice(
60
60
  |---|---|
61
61
  | `address` | Explicit ZMQ address, skipping the environment |
62
62
  | `env` | Environment variable to read, overriding the derived name |
63
- | `timeout_ms`, `attempts`, `backoff_s` | Forwarded to `connect_client` |
63
+ | `timeout_ms`, `attempts`, `backoff_s` | Forwarded to `connect_client` (first connect and every reconnect) |
64
+ | `connect_deadline_s` | Budget for the *first* connect (default 10 s); past it the constructor raises `DeviceUnreachableError` |
65
+ | `requires` | Remote methods the server must expose; checked by `preflight()` (a wrong address connects fine but is not this device) |
64
66
 
65
67
  The address comes from `address` if given, else the environment; an unset
66
68
  variable raises a `ValueError` naming the variable to set.
@@ -106,10 +108,48 @@ explicitly.
106
108
  `connect_client(address)` is the module-level builder used for the rebuilds:
107
109
  `build_client` plus a connection-handshake retry with linear backoff, which
108
110
  matters when several clients open back-to-back against the same server.
111
+ `reconnect(dev)` rebuilds with the sub-device's own `timeout_ms` /
112
+ `attempts` / `backoff_s`, not the defaults.
109
113
 
110
114
  Every multi-device composite gets this robustness for free instead of
111
115
  re-implementing it per module.
112
116
 
117
+ ## Connection state and a bounded first connect
118
+
119
+ The composite is never silent about its servers. Constructing it logs one
120
+ line per sub-device (`Connecting hwp @ tcp://… (timeout 5000 ms) …`,
121
+ `Connected hwp @ tcp://… in 0.4 s`, or `Connect to tcp://… failed, attempt
122
+ 1/4: … ; retrying in 2 s`) and a summary (`PolPlRig ready in 1.2 s:
123
+ hwp=tcp://…, spec=tcp://…`) — the console sink is switched on by the
124
+ composite itself (`ensure_logging()`), so this shows even when the composite
125
+ is built before the experiment that would configure logging.
126
+
127
+ The **first connect is bounded**: each configured sub-device gets
128
+ `connect_deadline_s` (default 10 s); a server that does not answer within it
129
+ raises `DeviceUnreachableError(dev, address, elapsed_s)` from the constructor
130
+ instead of grinding through attempts × timeout × backoff. Under thread
131
+ affinity the first connects run in parallel on the sub-devices' own threads,
132
+ so three dead servers cost one deadline, not three. Reconnects during a run
133
+ keep the attempt ladder — a server busy finishing a long move must be waited
134
+ for, not declared dead.
135
+
136
+ Each sub-device carries a `ConnectionState` — `CONNECTING → CONNECTED →
137
+ DEGRADED` (a call timed out, the client is being rebuilt) `→ DISCONNECTED`
138
+ (connect/reconnect failed, or `disconnect_all()`):
139
+
140
+ ```python
141
+ composite.status() # {"hwp": ConnectionState.CONNECTED, "spec": ...}
142
+ composite.status("hwp") # ConnectionState.CONNECTED
143
+
144
+ @composite.on_status_change
145
+ def _show(dev, old, new, detail):
146
+ ui.set_led(dev, new) # runs on the thread that changed the state
147
+ ```
148
+
149
+ Every transition is logged (INFO, or WARNING for `DEGRADED` /
150
+ `DISCONNECTED`) whether or not anyone subscribes; a subscriber that raises
151
+ is logged and ignored — a broken UI hook must not break the rig.
152
+
113
153
  ## One thread per sub-device
114
154
 
115
155
  ZMQ sockets are not thread-safe, and even migrating one between threads is
@@ -161,6 +201,16 @@ for a data directory either fails or means nothing:
161
201
  composite.set_data_path(frames_dir, devices=["spec"])
162
202
  ```
163
203
 
204
+ ## Preflight
205
+
206
+ `preflight()` proves each sub-device is the device it is supposed to be and
207
+ answers: a remote sub-device must expose every method in its `requires`
208
+ list and its `identity()` must reply; a local one must have the required
209
+ attributes. It returns `{name: problem}` for the failures (empty when the
210
+ rig is ready) and logs one line per sub-device either way. `Experiment`
211
+ runs it in `setup()`; `plesty.lib.experiment.connect_rig` runs it at
212
+ start-up and asks the operator what to do (see the experiment guide).
213
+
164
214
  ## Interplay with experiments
165
215
 
166
216
  `Experiment` accepts a `CompositeDevice` at construction; its default
@@ -84,6 +84,53 @@ Step methods may be sync or async; results are awaited automatically. Each
84
84
  step is retried (`max_retries`, `retry_sleep` constructor arguments) before
85
85
  the run aborts.
86
86
 
87
+ ## Starting on a rig that is really there
88
+
89
+ An experiment's `__main__` builds the rig with `connect_rig` — the same
90
+ start for every experiment: connect the servers, run the composite's
91
+ `preflight()` (each sub-device exposes the methods its configuration
92
+ `requires` and answers `identity()`), and if anything is missing, tell the
93
+ operator which device, at which address, from which variable — and ask:
94
+
95
+ ```python
96
+ from plesty.lib.experiment import connect_rig
97
+
98
+ rig, skipped = connect_rig(
99
+ lambda skip: PolPlRig(with_powermeter="pm" not in skip),
100
+ optional={"pm"}, # the experiment can run without it
101
+ )
102
+ ```
103
+
104
+ ```text
105
+ The rig is not ready — 1 device(s) failed the preflight:
106
+ - pm: no answer from tcp://127.0.0.1:5557: TimeoutError('Server did not respond in time')
107
+ address tcp://127.0.0.1:5557 (from PM_ADDRESS in .env / the environment)
108
+ Check that the pm server(s) are running and that PM_ADDRESS point at them
109
+ (a wrong port connects fine but is not the expected device).
110
+ [r] retry (after fixing .env or starting the server) [a] abort [s] continue without pm
111
+ >
112
+ ```
113
+
114
+ `r` rebuilds the rig (an edited `.env` or a freshly started server is picked
115
+ up), `a` raises `RigNotReadyError`, `s` — offered only when every failing
116
+ sub-device is in `optional` — returns the rig without them and names them in
117
+ `skipped`, so the experiment can drop the matching part of its plan. Without
118
+ a terminal on stdin nothing waits: the diagnosis is logged and
119
+ `RigNotReadyError` raised.
120
+
121
+ The check also runs inside `Experiment.setup()`: a run never starts on a
122
+ composite whose `preflight()` reports a problem, whether or not `connect_rig`
123
+ was used. Declare what each sub-device must be able to do in its
124
+ configuration — that is what turns "the port answers" into "it is the
125
+ stage":
126
+
127
+ ```yaml
128
+ devices:
129
+ hwp: {timeout_ms: 5000, requires: [home_stage, move_absolute, get_position]}
130
+ spec: {timeout_ms: 5000, requires: [acquire, get_recent_file, write, query]}
131
+ pm: {timeout_ms: 5000, requires: [measure_power, write]}
132
+ ```
133
+
87
134
  ## Data on a shared disk
88
135
 
89
136
  Blobs a device writes itself (spectrometer frames, camera images) never
@@ -19,19 +19,19 @@
19
19
  "version": "8.30.1"
20
20
  },
21
21
  "type": "secret_detection",
22
- "start_time": "2026-08-17T12:45:23",
23
- "end_time": "2026-08-17T12:45:24",
22
+ "start_time": "2026-08-17T15:29:32",
23
+ "end_time": "2026-08-17T15:29:32",
24
24
  "status": "success",
25
25
  "observability": {
26
26
  "events": [
27
27
  {
28
28
  "event": "collect_secrets_analyzer_scan_metrics_from_pipeline",
29
- "time_s": 0.469797198,
29
+ "time_s": 0.45769703,
30
30
  "exit_code": 0,
31
31
  "git_strategy": "FetchShallow",
32
- "repo_size_kb": 685,
32
+ "repo_size_kb": 708,
33
33
  "commit_count": 1,
34
- "bytes_scanned": 314,
34
+ "bytes_scanned": 220,
35
35
  "pipeline_type": "Tag"
36
36
  },
37
37
  {