apsbits 1.0.6__tar.gz → 2.0.1__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.
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/workflows/code.yml +2 -2
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/workflows/docs.yml +1 -1
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/workflows/pypi.yml +1 -1
- {apsbits-1.0.6 → apsbits-2.0.1}/.gitignore +1 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/HISTORY.rst +19 -1
- {apsbits-1.0.6 → apsbits-2.0.1}/PKG-INFO +2 -1
- {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/PKG-INFO +2 -1
- {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/SOURCES.txt +2 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/requires.txt +1 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/startup.rst +1 -1
- {apsbits-1.0.6 → apsbits-2.0.1}/pyproject.toml +1 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/_version.py +3 -3
- apsbits-2.0.1/src/apsbits/core/catalog_init.py +143 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/core/run_engine_init.py +3 -7
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/iconfig.yml +5 -1
- apsbits-2.0.1/src/apsbits/tests/conftest.py +124 -0
- apsbits-2.0.1/src/apsbits/tests/test_catalog_init.py +121 -0
- apsbits-2.0.1/src/apsbits/tests/test_controls_setup.py +98 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_general.py +6 -5
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/controls_setup.py +52 -19
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/helper_functions.py +3 -3
- apsbits-1.0.6/src/apsbits/core/catalog_init.py +0 -60
- apsbits-1.0.6/src/apsbits/tests/conftest.py +0 -44
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/other.md +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/question-issue-template.md +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/.github/dependabot.yml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/.pre-commit-config.yaml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/LICENSE +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/README.md +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/dependency_links.txt +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/entry_points.txt +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/top_level.txt +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/Makefile +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/make.bat +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/resources/create-repository-name.webp +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/resources/create-repository-owner.webp +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/resources/demo.ipynb +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/resources/use-this-template-button.webp +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/_static/.gitkeep +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/api.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/core.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/demo_instrument.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/demo_qserver.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.core.best_effort_init.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.core.catalog_init.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.core.run_engine_init.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.callbacks.demo_nexus_callback.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.callbacks.demo_spec_callback.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.callbacks.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.configs.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.devices.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.plans.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.plans.sim_plans.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.startup.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_qserver.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.config_loaders.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.controls_setup.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.helper_functions.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.logging_setup.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.metadata.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.stored_dict.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/index.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/utils.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/bits_overview.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/conf.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/console.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/dm.md +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/logging_config.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/notebook.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/script.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/creating_devices.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/creating_instrument.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/developing_bits.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/dm.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/index.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/logging.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/qserver.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/qserver_service.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/sessions.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/setting_iconfig.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/template_creation.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/history.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/index.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/install.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/license.rst +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/setup.cfg +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/api/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/api/create_new_instrument.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/api/delete_instrument.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/api/run_instrument.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/configs/logging.yml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/core/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/core/best_effort_init.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/core/instrument_init.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/README.md +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/callbacks/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/callbacks/demo_nexus_callback.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/callbacks/demo_spec_callback.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/devices.yml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/devices_aps_only.yml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/extra_logging.yml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/devices/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/plans/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/plans/sim_plans.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/qserver/qs-config.yml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/qserver/user_group_permissions.yaml +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/startup.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/suspenders/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/utils/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_scripts/qs_host.sh +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/py.typed +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_config.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_delete_instrument.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_device_factories.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_make_devices.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_run_instrument.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_stored_dict.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/__init__.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/aps_functions.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/baseline_setup.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/config_loaders.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/logging_setup.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/metadata.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/sim_creator.py +0 -0
- {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/stored_dict.py +0 -0
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
runs-on: ubuntu-latest
|
|
21
21
|
|
|
22
22
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
24
|
|
|
25
25
|
- uses: actions/setup-python@v6
|
|
26
26
|
with:
|
|
@@ -51,7 +51,7 @@ jobs:
|
|
|
51
51
|
DISPLAY: ":99.0"
|
|
52
52
|
|
|
53
53
|
steps:
|
|
54
|
-
- uses: actions/checkout@
|
|
54
|
+
- uses: actions/checkout@v6
|
|
55
55
|
|
|
56
56
|
- name: Install OS libraries to test Linux PyQt apps
|
|
57
57
|
run: |
|
|
@@ -30,7 +30,7 @@ describe future plans.
|
|
|
30
30
|
.. Coming release content can be gathered here.
|
|
31
31
|
Some people object to publishing unreleased changes.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
2.0.2
|
|
34
34
|
#####
|
|
35
35
|
|
|
36
36
|
release expected ?
|
|
@@ -46,6 +46,24 @@ describe future plans.
|
|
|
46
46
|
* Bump iconfig version to 2.0.1 for the baseline addition.
|
|
47
47
|
* Remove run_engine section from QS config.yml file and pin QS to 0.0.22+.
|
|
48
48
|
|
|
49
|
+
2.0.1
|
|
50
|
+
#####
|
|
51
|
+
|
|
52
|
+
released 2026-01-13
|
|
53
|
+
|
|
54
|
+
Enhancements
|
|
55
|
+
------------
|
|
56
|
+
|
|
57
|
+
* add support for TILED_PROFILE_NAME & TILED_PATH_NAME
|
|
58
|
+
|
|
59
|
+
2.0.0 (PyPI) and 1.0.6 (repository)
|
|
60
|
+
###################################
|
|
61
|
+
|
|
62
|
+
released 2025-10-21
|
|
63
|
+
|
|
64
|
+
Note that PyPI version (2.0.0) is different than repository (1.0.6).
|
|
65
|
+
They share the same source code hash (``484b02f1537301``).
|
|
66
|
+
|
|
49
67
|
1.0.4
|
|
50
68
|
#####
|
|
51
69
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apsbits
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: Model of a Bluesky Data Acquisition Instrument in console, notebook, & queueserver.
|
|
5
5
|
Author-email: Eric Codrea <ecodrea@anl.gov>, Pete Jemian <prjemian+instrument@gmail.com>, Rafael Vescovi <rvescovi@anl.gov>
|
|
6
6
|
Maintainer-email: Eric Codrea <ecodrea@anl.gov>, Pete Jemian <prjemian+instrument@gmail.com>, Rafael Vescovi <rvescovi@anl.gov>
|
|
@@ -20,6 +20,7 @@ License-File: LICENSE
|
|
|
20
20
|
Requires-Dist: apstools
|
|
21
21
|
Requires-Dist: bluesky-queueserver>=0.0.22
|
|
22
22
|
Requires-Dist: bluesky-queueserver-api
|
|
23
|
+
Requires-Dist: bluesky-tiled-plugins
|
|
23
24
|
Requires-Dist: bluesky-widgets
|
|
24
25
|
Requires-Dist: bluesky
|
|
25
26
|
Requires-Dist: caproto
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apsbits
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: Model of a Bluesky Data Acquisition Instrument in console, notebook, & queueserver.
|
|
5
5
|
Author-email: Eric Codrea <ecodrea@anl.gov>, Pete Jemian <prjemian+instrument@gmail.com>, Rafael Vescovi <rvescovi@anl.gov>
|
|
6
6
|
Maintainer-email: Eric Codrea <ecodrea@anl.gov>, Pete Jemian <prjemian+instrument@gmail.com>, Rafael Vescovi <rvescovi@anl.gov>
|
|
@@ -20,6 +20,7 @@ License-File: LICENSE
|
|
|
20
20
|
Requires-Dist: apstools
|
|
21
21
|
Requires-Dist: bluesky-queueserver>=0.0.22
|
|
22
22
|
Requires-Dist: bluesky-queueserver-api
|
|
23
|
+
Requires-Dist: bluesky-tiled-plugins
|
|
23
24
|
Requires-Dist: bluesky-widgets
|
|
24
25
|
Requires-Dist: bluesky
|
|
25
26
|
Requires-Dist: caproto
|
|
@@ -109,7 +109,9 @@ src/apsbits/demo_instrument/utils/__init__.py
|
|
|
109
109
|
src/apsbits/demo_scripts/qs_host.sh
|
|
110
110
|
src/apsbits/tests/__init__.py
|
|
111
111
|
src/apsbits/tests/conftest.py
|
|
112
|
+
src/apsbits/tests/test_catalog_init.py
|
|
112
113
|
src/apsbits/tests/test_config.py
|
|
114
|
+
src/apsbits/tests/test_controls_setup.py
|
|
113
115
|
src/apsbits/tests/test_delete_instrument.py
|
|
114
116
|
src/apsbits/tests/test_device_factories.py
|
|
115
117
|
src/apsbits/tests/test_general.py
|
|
@@ -70,7 +70,7 @@ Bluesky Initialization
|
|
|
70
70
|
|
|
71
71
|
bec, peaks = init_bec_peaks(iconfig)
|
|
72
72
|
cat = init_catalog(iconfig)
|
|
73
|
-
RE, sd = init_RE(iconfig,
|
|
73
|
+
RE, sd = init_RE(iconfig, subscribers=[bec, cat])
|
|
74
74
|
|
|
75
75
|
This block initializes:
|
|
76
76
|
* Best Effort Callback (BEC) and peak finding
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '
|
|
32
|
-
__version_tuple__ = version_tuple = (
|
|
31
|
+
__version__ = version = '2.0.1'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 0, 1)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'ge2e0b30f3'
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Databroker catalog
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
.. autosummary::
|
|
6
|
+
~init_catalog
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import weakref
|
|
11
|
+
from typing import Any
|
|
12
|
+
from typing import Union
|
|
13
|
+
|
|
14
|
+
import databroker
|
|
15
|
+
from bluesky_tiled_plugins.clients.catalog_of_bluesky_runs import CatalogOfBlueskyRuns
|
|
16
|
+
from databroker._drivers.mongo_normalized import BlueskyMongoCatalog
|
|
17
|
+
from databroker._drivers.msgpack import BlueskyMsgpackCatalog
|
|
18
|
+
from tiled.client import from_profile
|
|
19
|
+
from tiled.client import from_uri
|
|
20
|
+
from tiled.client.container import Container
|
|
21
|
+
from tiled.server import SimpleTiledServer
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
logger.bsdev(__file__)
|
|
25
|
+
|
|
26
|
+
# The httpx (via tiled) logger is set too noisy. Make it quieter.
|
|
27
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
28
|
+
|
|
29
|
+
DATABROKER_CATALOG_TYPE = Union[BlueskyMongoCatalog, BlueskyMsgpackCatalog]
|
|
30
|
+
TILED_CATALOG_TYPE = Union[CatalogOfBlueskyRuns, Container]
|
|
31
|
+
ANY_CATALOG_TYPE = Union[DATABROKER_CATALOG_TYPE, TILED_CATALOG_TYPE]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def init_catalog(iconfig: dict[str, Any]) -> ANY_CATALOG_TYPE:
|
|
35
|
+
"""
|
|
36
|
+
Setup for a catalog to record bluesky run documents.
|
|
37
|
+
|
|
38
|
+
Return only one catalog object, depending on the keys in 'iconfig'.
|
|
39
|
+
The object returned is the first successful match, in this order:
|
|
40
|
+
|
|
41
|
+
* tiled catalog: requires TILED_PROFILE_NAME and optional TILED_PATH_NAME
|
|
42
|
+
* databroker catalog: requires DATABROKER_CATALOG
|
|
43
|
+
* temporary databroker catalog: fallback is the above are not successful
|
|
44
|
+
* (TODO) temporary tiled catalog: replaces temporary databroker fallback
|
|
45
|
+
"""
|
|
46
|
+
handlers = [ # try these, in order
|
|
47
|
+
_tiled_profile_client,
|
|
48
|
+
_databroker_named_catalog,
|
|
49
|
+
# fallbacks
|
|
50
|
+
_databroker_temporary_catalog,
|
|
51
|
+
# TODO: promote once `del client` is not needed for exit
|
|
52
|
+
_tiled_temporary_catalog,
|
|
53
|
+
]
|
|
54
|
+
for handler in handlers:
|
|
55
|
+
try:
|
|
56
|
+
cat = handler(iconfig)
|
|
57
|
+
if cat is None:
|
|
58
|
+
continue
|
|
59
|
+
return cat
|
|
60
|
+
except Exception as exinfo:
|
|
61
|
+
logger.error(
|
|
62
|
+
"%s() Failed to create catalog: %s",
|
|
63
|
+
handler.__name__,
|
|
64
|
+
str(exinfo),
|
|
65
|
+
)
|
|
66
|
+
raise RuntimeError("Could not create a catalog for Bluesky run documents.")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _databroker_named_catalog(
|
|
70
|
+
iconfig: dict[str, Any],
|
|
71
|
+
) -> Union[
|
|
72
|
+
DATABROKER_CATALOG_TYPE,
|
|
73
|
+
None,
|
|
74
|
+
]:
|
|
75
|
+
"""Connect with a named databroker catalog."""
|
|
76
|
+
cat = None
|
|
77
|
+
catalog_name = iconfig.get("DATABROKER_CATALOG")
|
|
78
|
+
if catalog_name is not None:
|
|
79
|
+
cat = databroker.catalog[catalog_name].v2
|
|
80
|
+
logger.debug("%s: cat=%s", type(cat).__name__, str(cat))
|
|
81
|
+
if cat is not None:
|
|
82
|
+
logger.info("Databroker catalog initialized: %s", cat.name)
|
|
83
|
+
return cat
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _databroker_temporary_catalog(iconfig: dict[str, Any]) -> BlueskyMsgpackCatalog:
|
|
87
|
+
"""Connect with a temporary databroker catalog."""
|
|
88
|
+
cat = databroker.temp().v2
|
|
89
|
+
logger.debug("%s: cat=%s", type(cat).__name__, str(cat))
|
|
90
|
+
logger.info("Databroker temporary catalog initialized")
|
|
91
|
+
return cat
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _tiled_profile_client(iconfig: dict[str, Any]) -> Union[None, TILED_CATALOG_TYPE]:
|
|
95
|
+
"""Connect with a tiled server using a profile."""
|
|
96
|
+
cat = None
|
|
97
|
+
profile = iconfig.get("TILED_PROFILE_NAME")
|
|
98
|
+
path = iconfig.get("TILED_PATH_NAME")
|
|
99
|
+
if profile is not None:
|
|
100
|
+
client = from_profile(profile)
|
|
101
|
+
cat = client if path is None else client[path]
|
|
102
|
+
|
|
103
|
+
logger.debug("%s: cat=%s", type(cat).__name__, str(cat))
|
|
104
|
+
if cat is not None:
|
|
105
|
+
logger.info(
|
|
106
|
+
"Tiled server (catalog) connected, profile=%r, path=%r",
|
|
107
|
+
profile,
|
|
108
|
+
path,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return cat
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _tiled_temporary_catalog(iconfig: dict[str, Any]) -> Container:
|
|
115
|
+
"""Connect with a temporary tiled catalog.
|
|
116
|
+
|
|
117
|
+
WARNING: The SimpleTiledServer creates background threads that may prevent
|
|
118
|
+
clean process exit. For interactive use, explicitly delete the returned
|
|
119
|
+
client when done: `del client`
|
|
120
|
+
"""
|
|
121
|
+
save_path = iconfig.get("TILED_SAVE_PATH") # testing only?
|
|
122
|
+
server = SimpleTiledServer(save_path)
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
client = from_uri(server.uri)
|
|
126
|
+
logger.info("Tiled server (temporary catalog) connected")
|
|
127
|
+
|
|
128
|
+
# Store server reference for cleanup when client is deleted
|
|
129
|
+
client._tiled_server = server
|
|
130
|
+
|
|
131
|
+
# Cleanup when client is garbage collected
|
|
132
|
+
def cleanup():
|
|
133
|
+
try:
|
|
134
|
+
server.close()
|
|
135
|
+
except Exception:
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
weakref.finalize(client, cleanup)
|
|
139
|
+
|
|
140
|
+
return client
|
|
141
|
+
except Exception:
|
|
142
|
+
server.close()
|
|
143
|
+
raise
|
|
@@ -19,8 +19,8 @@ import bluesky
|
|
|
19
19
|
import databroker._drivers.mongo_normalized
|
|
20
20
|
import databroker._drivers.msgpack
|
|
21
21
|
import tiled
|
|
22
|
-
from bluesky.callbacks.tiled_writer import TiledWriter
|
|
23
22
|
from bluesky.utils import ProgressBarManager
|
|
23
|
+
from bluesky_tiled_plugins import TiledWriter
|
|
24
24
|
|
|
25
25
|
from apsbits.utils.controls_setup import connect_scan_id_pv
|
|
26
26
|
from apsbits.utils.controls_setup import set_control_layer
|
|
@@ -36,7 +36,6 @@ logger.bsdev(__file__)
|
|
|
36
36
|
def init_RE(
|
|
37
37
|
iconfig: collections.abc.Mapping[str, Any],
|
|
38
38
|
subscribers: Optional[list[Any]] = None,
|
|
39
|
-
oregistry: Optional[Any] = None,
|
|
40
39
|
**kwargs: Any,
|
|
41
40
|
) -> tuple[bluesky.RunEngine, bluesky.SupplementalData]:
|
|
42
41
|
"""
|
|
@@ -66,9 +65,6 @@ def init_RE(
|
|
|
66
65
|
documentation.
|
|
67
66
|
Order in the list does not matter.
|
|
68
67
|
|
|
69
|
-
oregistry : Optional[Any], default=None
|
|
70
|
-
Registry instance for scan ID PV connection.
|
|
71
|
-
|
|
72
68
|
**kwargs: Additional keyword arguments passed to the RunEngine constructor.
|
|
73
69
|
For example, run_returns_result=True.
|
|
74
70
|
|
|
@@ -130,7 +126,7 @@ def init_RE(
|
|
|
130
126
|
# Check if it's a tiled client
|
|
131
127
|
if isinstance(instance, tiled.client.container.Container):
|
|
132
128
|
try:
|
|
133
|
-
tiled_writer = TiledWriter(instance)
|
|
129
|
+
tiled_writer = TiledWriter(instance, batch_size=1)
|
|
134
130
|
RE.subscribe(tiled_writer)
|
|
135
131
|
except Exception:
|
|
136
132
|
logger.exception(
|
|
@@ -172,7 +168,7 @@ def init_RE(
|
|
|
172
168
|
raise
|
|
173
169
|
|
|
174
170
|
scan_id_pv = iconfig.get("RUN_ENGINE", {}).get("SCAN_ID_PV")
|
|
175
|
-
connect_scan_id_pv(RE, pv=scan_id_pv
|
|
171
|
+
connect_scan_id_pv(RE, pv=scan_id_pv)
|
|
176
172
|
|
|
177
173
|
if re_config.get("USE_PROGRESS_BAR", True):
|
|
178
174
|
# Add a progress bar.
|
|
@@ -5,8 +5,12 @@ ICONFIG_VERSION: 2.0.1
|
|
|
5
5
|
|
|
6
6
|
# Add additional configuration for use with your instrument.
|
|
7
7
|
|
|
8
|
-
### The
|
|
8
|
+
### The databroker catalog name. (legacy use only)
|
|
9
9
|
DATABROKER_CATALOG: temp
|
|
10
|
+
### Tiled server profile and path name: (overrides DATABROKER_CATALOG key)
|
|
11
|
+
#TILED_PROFILE_NAME: raw
|
|
12
|
+
# Use TILED_PATH_NAME according to local tiled server configuration
|
|
13
|
+
#TILED_PATH_NAME: /raw
|
|
10
14
|
|
|
11
15
|
### RunEngine configuration
|
|
12
16
|
RUN_ENGINE:
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pytest fixtures for instrument tests.
|
|
3
|
+
|
|
4
|
+
This module provides fixtures for initializing the RunEngine with devices,
|
|
5
|
+
allowing tests to operate with device-dependent configurations without relying
|
|
6
|
+
on the production startup logic.
|
|
7
|
+
|
|
8
|
+
Fixtures:
|
|
9
|
+
runengine_with_devices: A RunEngine object in a session with devices configured.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import time
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
from apsbits.demo_instrument.startup import RE
|
|
19
|
+
from apsbits.demo_instrument.startup import make_devices
|
|
20
|
+
from apsbits.utils.config_loaders import load_config
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture(scope="session")
|
|
24
|
+
def runengine_with_devices() -> Any:
|
|
25
|
+
"""
|
|
26
|
+
Initialize the RunEngine with devices for testing.
|
|
27
|
+
|
|
28
|
+
This fixture calls RE with the `make_devices()` plan stub to mimic
|
|
29
|
+
the behavior previously performed in the startup module.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Any: An instance of the RunEngine with devices configured.
|
|
33
|
+
"""
|
|
34
|
+
# Load the configuration before testing
|
|
35
|
+
instrument_path = Path(__file__).parent.parent / "demo_instrument"
|
|
36
|
+
iconfig_path = instrument_path / "configs" / "iconfig.yml"
|
|
37
|
+
load_config(iconfig_path)
|
|
38
|
+
|
|
39
|
+
# Initialize instrument and make devices
|
|
40
|
+
from apsbits.core.instrument_init import init_instrument
|
|
41
|
+
|
|
42
|
+
instrument, oregistry = init_instrument("guarneri")
|
|
43
|
+
make_devices(file="devices.yml", device_manager=instrument)
|
|
44
|
+
|
|
45
|
+
return RE
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pytest.fixture(scope="session")
|
|
49
|
+
def ioc():
|
|
50
|
+
"""Run a softIoc in a subprocess.
|
|
51
|
+
|
|
52
|
+
Create a temporary EPICS database file that defines a long integer
|
|
53
|
+
record at "test:scan_id", start softIoc in a subprocess and yield
|
|
54
|
+
connection info for tests. Teardown stops the subprocess and
|
|
55
|
+
removes the temporary file.
|
|
56
|
+
"""
|
|
57
|
+
import os
|
|
58
|
+
import subprocess
|
|
59
|
+
import tempfile
|
|
60
|
+
|
|
61
|
+
# Minimal EPICS DB defining a longout record for 'test:scan_id'.
|
|
62
|
+
db_text = "\n".join(
|
|
63
|
+
[
|
|
64
|
+
'record(longout, "test:scan_id") {',
|
|
65
|
+
# .
|
|
66
|
+
' field(DESC, "scan id")',
|
|
67
|
+
" field(VAL, -10)",
|
|
68
|
+
"}",
|
|
69
|
+
]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Write DB to a temporary file that persists until teardown.
|
|
73
|
+
tf = tempfile.NamedTemporaryFile(mode="w", suffix=".db", delete=False)
|
|
74
|
+
try:
|
|
75
|
+
tf.write(db_text)
|
|
76
|
+
tf.flush()
|
|
77
|
+
tf.close()
|
|
78
|
+
|
|
79
|
+
# Start softIoc. Capture output so if it fails immediately we can
|
|
80
|
+
# surface useful error messages.
|
|
81
|
+
proc = subprocess.Popen(
|
|
82
|
+
[
|
|
83
|
+
"softIoc",
|
|
84
|
+
"-S",
|
|
85
|
+
"-d",
|
|
86
|
+
tf.name,
|
|
87
|
+
],
|
|
88
|
+
stdout=subprocess.PIPE,
|
|
89
|
+
stderr=subprocess.PIPE,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Wait briefly for the process to initialize. If it exits early,
|
|
93
|
+
# collect stdout/stderr and raise.
|
|
94
|
+
timeout = 5.0
|
|
95
|
+
poll = 0.0
|
|
96
|
+
interval = 0.05
|
|
97
|
+
while poll < timeout and proc.poll() is None:
|
|
98
|
+
time.sleep(interval)
|
|
99
|
+
poll += interval
|
|
100
|
+
|
|
101
|
+
if proc.poll() is not None:
|
|
102
|
+
out, err = proc.communicate(timeout=1)
|
|
103
|
+
raise RuntimeError(
|
|
104
|
+
"softIoc terminated unexpectedly. stdout: %r stderr: %r"
|
|
105
|
+
% (out.decode(errors="ignore"), err.decode(errors="ignore"))
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Provide connection info for tests.
|
|
109
|
+
yield dict(prefix="test1:", host="127.0.0.1", pv="test:scan_id")
|
|
110
|
+
|
|
111
|
+
finally:
|
|
112
|
+
# Teardown: terminate the softIoc subprocess and remove the DB file.
|
|
113
|
+
try:
|
|
114
|
+
proc.terminate()
|
|
115
|
+
proc.wait(timeout=2)
|
|
116
|
+
except Exception:
|
|
117
|
+
try:
|
|
118
|
+
proc.kill()
|
|
119
|
+
except Exception:
|
|
120
|
+
pass
|
|
121
|
+
try:
|
|
122
|
+
os.remove(tf.name)
|
|
123
|
+
except Exception:
|
|
124
|
+
pass
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Test the catalog_init module."""
|
|
2
|
+
|
|
3
|
+
from contextlib import nullcontext as does_not_raise
|
|
4
|
+
from unittest.mock import patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from tiled.profiles import ProfileNotFound
|
|
8
|
+
|
|
9
|
+
# Run these tests without running startup.py.
|
|
10
|
+
with patch("logging.Logger.bsdev"):
|
|
11
|
+
from apsbits.core.catalog_init import _databroker_named_catalog
|
|
12
|
+
from apsbits.core.catalog_init import _databroker_temporary_catalog
|
|
13
|
+
from apsbits.core.catalog_init import _tiled_profile_client
|
|
14
|
+
from apsbits.core.catalog_init import _tiled_temporary_catalog
|
|
15
|
+
from apsbits.core.catalog_init import init_catalog
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.parametrize(
|
|
19
|
+
"iconfig, handler, cat_type, context",
|
|
20
|
+
[
|
|
21
|
+
pytest.param(
|
|
22
|
+
{},
|
|
23
|
+
_databroker_temporary_catalog,
|
|
24
|
+
"BlueskyMsgpackCatalog",
|
|
25
|
+
does_not_raise(),
|
|
26
|
+
id="temporary databroker catalog",
|
|
27
|
+
),
|
|
28
|
+
pytest.param(
|
|
29
|
+
{},
|
|
30
|
+
init_catalog,
|
|
31
|
+
"BlueskyMsgpackCatalog",
|
|
32
|
+
does_not_raise(),
|
|
33
|
+
id="default to temporary databroker catalog",
|
|
34
|
+
),
|
|
35
|
+
pytest.param(
|
|
36
|
+
dict(
|
|
37
|
+
DATABROKER_CATALOG="no_such_catalog",
|
|
38
|
+
TILED_PROFILE_NAME="no_such_profile",
|
|
39
|
+
),
|
|
40
|
+
init_catalog,
|
|
41
|
+
"BlueskyMsgpackCatalog",
|
|
42
|
+
does_not_raise(),
|
|
43
|
+
id="invalid catalog & profile: fallback to temporary catalog",
|
|
44
|
+
),
|
|
45
|
+
pytest.param(
|
|
46
|
+
{},
|
|
47
|
+
_databroker_named_catalog,
|
|
48
|
+
"NoneType",
|
|
49
|
+
does_not_raise(),
|
|
50
|
+
id="no databroker catalog name",
|
|
51
|
+
),
|
|
52
|
+
pytest.param(
|
|
53
|
+
dict(DATABROKER_CATALOG="no_such_catalog"),
|
|
54
|
+
_databroker_named_catalog,
|
|
55
|
+
"ignored",
|
|
56
|
+
pytest.raises(KeyError, match="'no_such_catalog'"),
|
|
57
|
+
id="no such databroker catalog name",
|
|
58
|
+
),
|
|
59
|
+
pytest.param(
|
|
60
|
+
{},
|
|
61
|
+
_tiled_profile_client,
|
|
62
|
+
"NoneType",
|
|
63
|
+
does_not_raise(),
|
|
64
|
+
id="no tiled profile name",
|
|
65
|
+
),
|
|
66
|
+
pytest.param(
|
|
67
|
+
dict(TILED_PROFILE_NAME="no_such_profile"),
|
|
68
|
+
_tiled_profile_client,
|
|
69
|
+
"ignored",
|
|
70
|
+
pytest.raises(
|
|
71
|
+
ProfileNotFound,
|
|
72
|
+
match="Profile 'no_such_profile' not found.",
|
|
73
|
+
),
|
|
74
|
+
id="no such tiled profile name",
|
|
75
|
+
),
|
|
76
|
+
# TODO: _tiled_profile_client with:
|
|
77
|
+
# valid TILED_PROFILE_NAME
|
|
78
|
+
# valid TILED_PROFILE_NAME & valid TILED_PATH_NAME
|
|
79
|
+
# valid TILED_PROFILE_NAME & invalid TILED_PATH_NAME
|
|
80
|
+
pytest.param(
|
|
81
|
+
{},
|
|
82
|
+
_tiled_temporary_catalog,
|
|
83
|
+
"Container",
|
|
84
|
+
does_not_raise(),
|
|
85
|
+
id="temporary tiled catalog",
|
|
86
|
+
),
|
|
87
|
+
# TODO: _tiled_temporary_catalog & valid TILED_SAVE_PATH
|
|
88
|
+
# TODO: _tiled_temporary_catalog & invalid TILED_SAVE_PATH
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
def test_handlers(iconfig, handler, cat_type, context):
|
|
92
|
+
"""Test the handlers that create 'cat' objects."""
|
|
93
|
+
with context:
|
|
94
|
+
cat = handler(iconfig)
|
|
95
|
+
assert type(cat).__name__ == cat_type
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_use_temporary_tiled_catalog():
|
|
99
|
+
"""Typical use of the tiled temporary catalog."""
|
|
100
|
+
import bluesky
|
|
101
|
+
from bluesky_tiled_plugins import TiledWriter
|
|
102
|
+
from ophyd.sim import noisy_det
|
|
103
|
+
|
|
104
|
+
cat = _tiled_temporary_catalog({})
|
|
105
|
+
tw = TiledWriter(cat, batch_size=1)
|
|
106
|
+
RE = bluesky.RunEngine()
|
|
107
|
+
RE.subscribe(tw)
|
|
108
|
+
|
|
109
|
+
delay = 0.1
|
|
110
|
+
npts = 15
|
|
111
|
+
nruns = len(cat)
|
|
112
|
+
(uid,) = RE(bluesky.plans.count([noisy_det], num=npts, delay=delay))
|
|
113
|
+
assert isinstance(uid, str)
|
|
114
|
+
assert len(cat) == 1 + nruns
|
|
115
|
+
run = cat[uid]
|
|
116
|
+
assert run.stop["num_events"]["primary"] == npts
|
|
117
|
+
assert (run.stop["time"] - run.start["time"]) >= delay * npts
|
|
118
|
+
|
|
119
|
+
data = run.primary.read()
|
|
120
|
+
assert "noisy_det" in data
|
|
121
|
+
assert len(data["noisy_det"]) == npts
|