hutch-python 1.18.5__tar.gz → 1.19.0__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.
- {hutch-python-1.18.5 → hutch_python-1.19.0}/PKG-INFO +1 -1
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/releases.rst +22 -0
- hutch_python-1.19.0/docs/source/upcoming_release_notes/379-enh_limit_devices.rst +22 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/yaml_files.rst +18 -2
- hutch_python-1.19.0/hutch_python/_version.py +16 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/constants.py +1 -0
- hutch_python-1.19.0/hutch_python/epics_arch.py +483 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/happi.py +30 -15
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/load_conf.py +10 -2
- hutch_python-1.19.0/hutch_python/tests/happi_db.json +188 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_epics_arch.py +16 -19
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_happi.py +22 -3
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python.egg-info/PKG-INFO +1 -1
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python.egg-info/SOURCES.txt +1 -0
- hutch-python-1.18.5/hutch_python/_version.py +0 -4
- hutch-python-1.18.5/hutch_python/epics_arch.py +0 -202
- hutch-python-1.18.5/hutch_python/tests/happi_db.json +0 -95
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.coveragerc +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.flake8 +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.git_archival.txt +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.gitattributes +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.github/ISSUE_TEMPLATE.md +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.github/workflows/standard.yml +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.gitignore +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.landscape.yml +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/.pre-commit-config.yaml +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/AUTHORS.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/CONTRIBUTING.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/LICENSE +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/MANIFEST.in +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/README.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/bin/hutch-python +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/conda-recipe/meta.yaml +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/dev-requirements.txt +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/Makefile +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/pre-release-notes.sh +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/release_notes.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/beamline.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/bug.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/cache.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/cli.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/conf.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/daq.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/database.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/debug.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/environment.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/experiment.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/hutch_setup.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/index.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/ipython_log.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/load_conf.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/load_parts.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/log_setup.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/namespace.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/object_configuration.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/other_modules.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/presets.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/report.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/start.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/startup.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/tips.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/tutorial.ipynb +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/upcoming_release_notes/template-full.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/upcoming_release_notes/template-short.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/user_utils.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs/source/utils.rst +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/docs-requirements.txt +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/__init__.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/__main__.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/bug.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cache.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/calc_defaults.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cam_load.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cli.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/cookiecutter.json +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/.gitignore +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/README.md +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/conf.yml +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/experiments/__init__.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/set-dev +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/{{ cookiecutter.hutch }}/__init__.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/{{ cookiecutter.hutch }}/beamline.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/{{ cookiecutter.hutch }}env +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/{{ cookiecutter.hutch }}python +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/debug.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/env_version.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/exp_load.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/ipython_log.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/lcls.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/log_setup.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/logging.yml +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/mpl_config.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/namespace.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/obj_config.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/ophyd_settings.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/options.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/plan_defaults.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/plan_wrappers.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/print_hint_banner.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/pt_app_config.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/qs_load.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/sim.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/startup_script.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/templates/issue.template +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/__init__.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/bad_script.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/camviewer.cfg +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/conf.yaml +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/conftest.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/envs/pcds-1.0.0 +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/envs/pcds-2.0.0 +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/envs/z-env +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/experiments/sample_expname.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/experiments/x010.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/experiments/x011.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/sample_module_1.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/sample_module_2.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/script.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_bug.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_cache.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_cam_load.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_cli.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_experiment.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_ipython_log.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_load_conf.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_log_setup.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_namespace.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_obj_config.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_plan_wrappers.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_questionnaire.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_tstpython.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_user_load.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/test_utils.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/tst/__init__.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/tst/beamline.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/tests/tstpython +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/user_load.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/utils.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python/version.py +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python.egg-info/dependency_links.txt +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python.egg-info/entry_points.txt +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python.egg-info/requires.txt +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/hutch_python.egg-info/top_level.txt +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/pyproject.toml +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/requirements.txt +0 -0
- {hutch-python-1.18.5 → hutch_python-1.19.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hutch-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.19.0
|
|
4
4
|
Summary: Launcher and Config Reader for LCLS Interactive IPython Sessions
|
|
5
5
|
Author: SLAC National Accelerator Laboratory
|
|
6
6
|
License: Copyright (c) 2023, The Board of Trustees of the Leland Stanford Junior
|
|
@@ -2,6 +2,28 @@ Release History
|
|
|
2
2
|
###############
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
v1.19.0 (2024-04-15)
|
|
6
|
+
====================
|
|
7
|
+
|
|
8
|
+
Features
|
|
9
|
+
--------
|
|
10
|
+
- Updates EpicsArch script, adding several debug/testin flags and ensuring
|
|
11
|
+
information is up-to-date. Includes:
|
|
12
|
+
|
|
13
|
+
- ``update_file``: feature that updates EpicsArch based on current questionnaire
|
|
14
|
+
- ``--softlink``: point arch file to new experiment via symbolic link
|
|
15
|
+
- ``--level``: specify a debug level
|
|
16
|
+
- ``--cds-items``: displays questionnaire data for a given run and experiment
|
|
17
|
+
- ``--link-path``: allows user to provide custom filepath for softlinks
|
|
18
|
+
- Adds load_level conf.yaml key for choosing the method used to gather happi devices
|
|
19
|
+
|
|
20
|
+
Contributors
|
|
21
|
+
------------
|
|
22
|
+
- c-tsoi
|
|
23
|
+
- tangkong
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
5
27
|
v1.18.5 (2023-09-14)
|
|
6
28
|
====================
|
|
7
29
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
379 enh_limit_devices
|
|
2
|
+
#####################
|
|
3
|
+
|
|
4
|
+
API Changes
|
|
5
|
+
-----------
|
|
6
|
+
- N/A
|
|
7
|
+
|
|
8
|
+
Features
|
|
9
|
+
--------
|
|
10
|
+
- Adds load_level conf.yaml key for choosing the method used to gather happi devices
|
|
11
|
+
|
|
12
|
+
Bugfixes
|
|
13
|
+
--------
|
|
14
|
+
- N/A
|
|
15
|
+
|
|
16
|
+
Maintenance
|
|
17
|
+
-----------
|
|
18
|
+
- N/A
|
|
19
|
+
|
|
20
|
+
Contributors
|
|
21
|
+
------------
|
|
22
|
+
- tangkong
|
|
@@ -3,8 +3,8 @@ Yaml Files
|
|
|
3
3
|
|
|
4
4
|
``hutch-python`` uses a ``conf.yml`` file for basic configuration. This is a
|
|
5
5
|
standard yaml file with the following valid keys:
|
|
6
|
-
``hutch``, ``db``, ``load``, ``
|
|
7
|
-
``daq_host``, and ``daq_platform``.
|
|
6
|
+
``hutch``, ``db``, ``load``, ``load_level``, ``experiment``, ``obj_config``,
|
|
7
|
+
``daq_type``, ``daq_host``, and ``daq_platform``.
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
hutch
|
|
@@ -69,6 +69,22 @@ This key is used to include hutch-specific code.
|
|
|
69
69
|
``from module import *`` for each of these modules.
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
load_level
|
|
73
|
+
----------
|
|
74
|
+
The ``load_level`` key expects one of the following strings, corresponding to the
|
|
75
|
+
amount of ophyd devices to load:
|
|
76
|
+
|
|
77
|
+
- ``UPSTREAM``: The hutch's devices, and devices upstream from the requested hutch.
|
|
78
|
+
If there are multiple paths to the requested hutch, all paths' devices are loaded.
|
|
79
|
+
- ``STANDARD``: Devices gathered via ``UPSTREAM``, plus devices that share the
|
|
80
|
+
"beamline" field in happi with the ``UPSTREAM`` devices. (The current standard)
|
|
81
|
+
- ``ALL``: All devices in the happi database. Use this option at your own risk.
|
|
82
|
+
|
|
83
|
+
.. code-block:: YAML
|
|
84
|
+
|
|
85
|
+
load_level: UPSTREAM
|
|
86
|
+
|
|
87
|
+
|
|
72
88
|
experiment
|
|
73
89
|
----------
|
|
74
90
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '1.19.0'
|
|
16
|
+
__version_tuple__ = version_tuple = (1, 19, 0)
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
"""Module to help create the epicsArch file that will be read by the DAQ."""
|
|
2
|
+
import argparse
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
from prettytable import PrettyTable
|
|
11
|
+
|
|
12
|
+
from .constants import EPICS_ARCH_FILE_PATH
|
|
13
|
+
from .qs_load import get_qs_client
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
import psdm_qs_cli
|
|
17
|
+
from psdm_qs_cli import QuestionnaireClient
|
|
18
|
+
except ImportError:
|
|
19
|
+
psdm_qs_cli = None
|
|
20
|
+
QuestionnaireClient = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Annotation with dataclass, making struct to help organize cds objects in prettytable
|
|
24
|
+
@dataclass
|
|
25
|
+
class QStruct:
|
|
26
|
+
alias: str
|
|
27
|
+
pvbase: str
|
|
28
|
+
pvtype: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _create_parser():
|
|
35
|
+
"""Argument Parser Setup. Define shell commands."""
|
|
36
|
+
parser = argparse.ArgumentParser(description='Create an epicsArch file '
|
|
37
|
+
'from the Quesionnaire')
|
|
38
|
+
|
|
39
|
+
parser.add_argument('experiment', help='Experiment name to'
|
|
40
|
+
' create the epicsArch file from. E.g.: xpplv6818')
|
|
41
|
+
|
|
42
|
+
parser.add_argument('--hutch', action="store",
|
|
43
|
+
help='Overrides hutch detection from the experiment '
|
|
44
|
+
'name. E.g.: xpp', default=None)
|
|
45
|
+
|
|
46
|
+
parser.add_argument('--path', action="store",
|
|
47
|
+
help='Directory path where to create the epicsArch '
|
|
48
|
+
'file. E.g.: /path/to/the/directory/', default=None)
|
|
49
|
+
|
|
50
|
+
parser.add_argument('--dry-run', action='store_true', default=False,
|
|
51
|
+
help='Print to stdout what would be written in the '
|
|
52
|
+
'archFile.')
|
|
53
|
+
|
|
54
|
+
parser.add_argument('--level', '-l', required=False, type=str, default="INFO",
|
|
55
|
+
help='Change the logging level, e.g. DEBUG to show the debug logging stream')
|
|
56
|
+
|
|
57
|
+
parser.add_argument('--cds-items', nargs=2, action='store', default=None,
|
|
58
|
+
help="Pulls all data from CDS tab in the form of a "
|
|
59
|
+
"dictionary. E.g.: xppx1003221 --cds-items run_xx "
|
|
60
|
+
"experiment_name, where xx is the run number. This option "
|
|
61
|
+
"will not automatically update the archfile.")
|
|
62
|
+
|
|
63
|
+
parser.add_argument('--softlink', '-sl', action='store_true', default=None,
|
|
64
|
+
help="create softlink for experiment. This is run after "
|
|
65
|
+
"the archfile has been updated/created.")
|
|
66
|
+
parser.add_argument('--link-path', '-sl_path', action='store',
|
|
67
|
+
default=EPICS_ARCH_FILE_PATH,
|
|
68
|
+
help="Provide user with option to supply custom path for "
|
|
69
|
+
"softlink. Defaults to: /cds/group/pcds/dist/pds/{}/misc/.")
|
|
70
|
+
return parser
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def main():
|
|
74
|
+
"""Entry point."""
|
|
75
|
+
parser = _create_parser()
|
|
76
|
+
parsed_args = parser.parse_args()
|
|
77
|
+
kwargs = vars(parsed_args)
|
|
78
|
+
logger_setup(parsed_args)
|
|
79
|
+
create_arch_file(**kwargs)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def logger_setup(args):
|
|
83
|
+
# Setting up the logger, to show the level when enabled
|
|
84
|
+
logging.getLogger().addHandler(logging.NullHandler())
|
|
85
|
+
logging.basicConfig(level="INFO")
|
|
86
|
+
logger.setLevel(args.level)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def create_arch_file(experiment, level=None, hutch=None, path=None, dry_run=False,
|
|
90
|
+
cds_items=None, softlink=None, link_path=None):
|
|
91
|
+
"""
|
|
92
|
+
Create an epicsArch file for the experiment.
|
|
93
|
+
|
|
94
|
+
Parameter
|
|
95
|
+
---------
|
|
96
|
+
experiment : str
|
|
97
|
+
Experiment name, e.g.: `xpplv6818`
|
|
98
|
+
hutch : str
|
|
99
|
+
Hutch name if other than the one from the experiment, e.g.: `xcs`
|
|
100
|
+
path : str
|
|
101
|
+
Directory where to create the file if other than the default one which
|
|
102
|
+
is `/cds/group/pcds/dist/pds/{}/misc/`
|
|
103
|
+
dry_run : bool
|
|
104
|
+
To indicate if only print to stdout the data that would be stored
|
|
105
|
+
in the epicsArch file and not create the file.
|
|
106
|
+
link_path : str
|
|
107
|
+
Path to overwrite softlink to experiment specific arch file.
|
|
108
|
+
cds_items : str
|
|
109
|
+
Pulls cds tab data in the form of a dictionary and prints this to Pretty Table.
|
|
110
|
+
|
|
111
|
+
Examples
|
|
112
|
+
--------
|
|
113
|
+
Create the epicsArch file for experiment `xpplv6818`. If no `--path` or
|
|
114
|
+
`--hutch` provided, write the file at the default path:
|
|
115
|
+
`/cds/group/pcds/dist/pds/{}/misc/` whre {} is the hutch name from the
|
|
116
|
+
experiment (`xpp`):
|
|
117
|
+
|
|
118
|
+
>>> epicsarch-qs xpplv6818
|
|
119
|
+
|
|
120
|
+
Would write: `/cds/group/pcds/dist/pds/xpp/misc/epicsArch_xpplv6818.txt`
|
|
121
|
+
|
|
122
|
+
To change the path where you want to write the epicsArch file:
|
|
123
|
+
|
|
124
|
+
>>> epicsarch-qs xpplv6818 --path /my/new/path/
|
|
125
|
+
|
|
126
|
+
Would write: `/my/new/path/epicsArch_xpplv6818.txt`
|
|
127
|
+
|
|
128
|
+
To change only the hutch name in the path:
|
|
129
|
+
|
|
130
|
+
>>> epicsarch-qs xpplv6818 --hutch xcs
|
|
131
|
+
|
|
132
|
+
Would write: `/cds/group/pcds/dist/pds/xcs/misc/epicsArch_xpplv6818.txt`
|
|
133
|
+
|
|
134
|
+
To print to standard output what would be written in the epicsArch file:
|
|
135
|
+
|
|
136
|
+
>>> epicsarch-qs xpplv6818 --dry-run
|
|
137
|
+
"""
|
|
138
|
+
file_path = None
|
|
139
|
+
if experiment and not dry_run:
|
|
140
|
+
# set the path to write the epicsArch file to.
|
|
141
|
+
if cds_items:
|
|
142
|
+
pull_cds_data(experiment, cds_items)
|
|
143
|
+
return
|
|
144
|
+
if path:
|
|
145
|
+
if path and not os.path.exists(path):
|
|
146
|
+
raise OSError('Invalid path: %s' % path)
|
|
147
|
+
file_path = path
|
|
148
|
+
elif hutch:
|
|
149
|
+
file_path = EPICS_ARCH_FILE_PATH.format(hutch.lower())
|
|
150
|
+
else:
|
|
151
|
+
file_path = EPICS_ARCH_FILE_PATH.format(experiment[0:3])
|
|
152
|
+
update_file(exp_name=experiment, path=file_path)
|
|
153
|
+
if softlink:
|
|
154
|
+
create_softlink(experiment, link_path)
|
|
155
|
+
elif dry_run:
|
|
156
|
+
print_dry_run(experiment)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def pull_cds_data(exp, run):
|
|
160
|
+
"""
|
|
161
|
+
Gather all user objects from the CDS tab in the questionnaire.
|
|
162
|
+
Parse objects and separate them based on type.
|
|
163
|
+
Display them in the console via PrettyTable.
|
|
164
|
+
|
|
165
|
+
Parameters
|
|
166
|
+
----------
|
|
167
|
+
exp: ``str``
|
|
168
|
+
The experiment's name e.g. xppx1003221
|
|
169
|
+
run: ''str''
|
|
170
|
+
The run number e.g. run21
|
|
171
|
+
|
|
172
|
+
Outputs
|
|
173
|
+
-------
|
|
174
|
+
PrettyTable visualization of cds objects
|
|
175
|
+
"""
|
|
176
|
+
# pull run data from questionnaire api, then take the data and sort it
|
|
177
|
+
# create PrettyTable instance and if the values from the run data contain
|
|
178
|
+
# pcdssetup then put them into a seperate dictionary as they are cds items
|
|
179
|
+
logger.debug('pull_cds_items(%s)', exp)
|
|
180
|
+
client = QuestionnaireClient()
|
|
181
|
+
logger.debug("in cds items, run numb:", str(run[1]))
|
|
182
|
+
runDetails_Dict = client.getProposalDetailsForRun(str(run[0]), str(run[1]))
|
|
183
|
+
sorted_runDetails_Dict = dict(sorted(runDetails_Dict.items()))
|
|
184
|
+
cds_dict = {}
|
|
185
|
+
myTable = PrettyTable(["Alias", "PV Base", "Type"])
|
|
186
|
+
for keys, vals in sorted_runDetails_Dict.items():
|
|
187
|
+
if "pcdssetup" in keys:
|
|
188
|
+
cds_dict[keys] = vals
|
|
189
|
+
|
|
190
|
+
# names are as follows:
|
|
191
|
+
# pcdssetup-motors, pcdssetup-areadet, pcdssetup-ao, pcdssetup-devs
|
|
192
|
+
# pcdssetup-ps, pcdssetup-trig, pcdssetup-vacuum, pcdssetup-temp
|
|
193
|
+
|
|
194
|
+
# iterate through all cds items and label them based on their type
|
|
195
|
+
# use the struct members to identify
|
|
196
|
+
displayList = []
|
|
197
|
+
for k, v in cds_dict.items():
|
|
198
|
+
if re.match('pcdssetup-motors.*-name', k):
|
|
199
|
+
pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
|
|
200
|
+
displayList.append(QStruct(v, pv, "motors"))
|
|
201
|
+
elif re.match('pcdssetup-areadet.*-name', k):
|
|
202
|
+
pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
|
|
203
|
+
displayList.append(QStruct(v, pv, "areadet"))
|
|
204
|
+
elif re.match('pcdssetup-ao.*-name', k):
|
|
205
|
+
pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
|
|
206
|
+
displayList.append(QStruct(v, pv, "analog output"))
|
|
207
|
+
elif re.match('pcdssetup-devs.*-name', k):
|
|
208
|
+
pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
|
|
209
|
+
displayList.append(QStruct(v, pv, "other devices"))
|
|
210
|
+
elif re.match('pcdssetup-ps.*-name', k):
|
|
211
|
+
pv = cds_dict.get(re.sub('name', 'pvname', k), '')
|
|
212
|
+
displayList.append(QStruct(v, pv, "power supplies"))
|
|
213
|
+
elif re.match('pcdssetup-trig.*-name', k):
|
|
214
|
+
pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
|
|
215
|
+
displayList.append(QStruct(v, pv, "triggers"))
|
|
216
|
+
elif re.match('pcdssetup-vacuum.*-name', k):
|
|
217
|
+
pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
|
|
218
|
+
displayList.append(QStruct(v, pv, "vacuum"))
|
|
219
|
+
elif re.match('pcdssetup-temp.*-name', k):
|
|
220
|
+
pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
|
|
221
|
+
displayList.append(QStruct(v, pv, "temperature"))
|
|
222
|
+
|
|
223
|
+
for struct in displayList:
|
|
224
|
+
myTable.add_row([struct.alias, struct.pvbase, struct.pvtype])
|
|
225
|
+
print(myTable)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def create_softlink(experiment, link_path):
|
|
229
|
+
"""
|
|
230
|
+
This removes the softlink in the /cds/group/pcds/dist/pds/{}/misc/ and
|
|
231
|
+
overwrites it with the new active experiment.
|
|
232
|
+
"""
|
|
233
|
+
# Defaults new softlink in /cds/group/pcds/dist/pds/{}/misc/
|
|
234
|
+
if not os.path.exists(link_path):
|
|
235
|
+
raise OSError('Path does not exist path: %s' % link_path)
|
|
236
|
+
|
|
237
|
+
subprocess.run(['ln', '-sf', link_path.format(experiment[0:3]) + 'epicsArch_'
|
|
238
|
+
+ experiment + '.txt', link_path.format(experiment[0:3])
|
|
239
|
+
+ 'epicsArch_' + experiment[0:3].upper() + '_exp_specific.txt'])
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def check_for_duplicates(qs_data, af_data):
|
|
243
|
+
"""
|
|
244
|
+
Check for duplicate PVs in the questionnaire, the code already throws a
|
|
245
|
+
warning for duplicate aliases. If duplicates (PV or Alias) are found in the
|
|
246
|
+
questionnaire throw error and prompt user to fix and re-run. If they are
|
|
247
|
+
found in the epicsArch file then step through each match and update
|
|
248
|
+
accordingly.
|
|
249
|
+
|
|
250
|
+
Parameters
|
|
251
|
+
----------
|
|
252
|
+
qs_data : list
|
|
253
|
+
af_data : list
|
|
254
|
+
|
|
255
|
+
Examples
|
|
256
|
+
--------
|
|
257
|
+
>>> epicsarch-qs xpplv6818 --dry-run
|
|
258
|
+
|
|
259
|
+
Returns
|
|
260
|
+
-------
|
|
261
|
+
updated_arch_list : list
|
|
262
|
+
Updated list containing sorted alias, PVs.
|
|
263
|
+
"""
|
|
264
|
+
# PART 1
|
|
265
|
+
# Convert lists to dictionaries to sort as a key - value pair while also
|
|
266
|
+
# removing any whitespice in the aliases.
|
|
267
|
+
|
|
268
|
+
# Questionnaire Data, removing whitespaces and newline chars
|
|
269
|
+
qsDict = dict(zip(qs_data[::2], qs_data[1::2]))
|
|
270
|
+
qsDict = {k.replace(" ", ""): v for k, v in qsDict.items()}
|
|
271
|
+
qsDict = {k.replace("\n", ""): v for k, v in qsDict.items()}
|
|
272
|
+
qsDict = {k: v.replace(" ", "") for k, v in qsDict.items()}
|
|
273
|
+
qsDict = {k: v.replace("\n", "") for k, v in qsDict.items()}
|
|
274
|
+
sorted_qsDict = dict(sorted(qsDict.items()))
|
|
275
|
+
|
|
276
|
+
# If the archfile is not empty then clean it if not ,skip
|
|
277
|
+
if len(af_data) > 0:
|
|
278
|
+
# ArchFile Data, removing whitespaces and newline chars
|
|
279
|
+
afDict = dict(zip(af_data[::2], af_data[1::2]))
|
|
280
|
+
afDict = {k.replace(" ", ""): v for k, v in afDict.items()}
|
|
281
|
+
afDict = {k.replace("\n", ""): v for k, v in afDict.items()}
|
|
282
|
+
afDict = {k: v.replace(" ", "") for k, v in afDict.items()}
|
|
283
|
+
afDict = {k: v.replace("\n", "") for k, v in afDict.items()}
|
|
284
|
+
sorted_afDict = dict(sorted(afDict.items()))
|
|
285
|
+
else:
|
|
286
|
+
afDict = {}
|
|
287
|
+
sorted_afDict = {}
|
|
288
|
+
|
|
289
|
+
# PART 2
|
|
290
|
+
# Check the questionaire for duplicate PVs
|
|
291
|
+
# Making reverse multidict to help identify duplicate values in questionnaire.
|
|
292
|
+
rev_keyDict = {}
|
|
293
|
+
for key, value in sorted_qsDict.items():
|
|
294
|
+
rev_keyDict.setdefault(value, list()).append(key)
|
|
295
|
+
|
|
296
|
+
pvDuplicate = [key for key, values in rev_keyDict.items() if len(values) > 1]
|
|
297
|
+
# Looking for duplicates of PVs in the questionaire
|
|
298
|
+
# also print out the alias for PV, change removing to warning operator to remove dup then rerun
|
|
299
|
+
for dup in pvDuplicate:
|
|
300
|
+
logger.debug("!Duplicate PV in questionnaire!:" + str(dup))
|
|
301
|
+
for value in rev_keyDict[dup][1:]:
|
|
302
|
+
logger.debug("Found PV duplicate(s) from questionnaire: " + value
|
|
303
|
+
+ ", " + sorted_qsDict[value])
|
|
304
|
+
raise ValueError("Please remove duplicates and re-run script!")
|
|
305
|
+
|
|
306
|
+
# Check to see if the archfile has any data in it
|
|
307
|
+
if len(af_data) == 0:
|
|
308
|
+
logger.debug("CFD: Case: no archfile given, returning cleaned questionnaire data.")
|
|
309
|
+
cleaned_qs_data = [x for item in sorted_qsDict.items() for x in item]
|
|
310
|
+
return cleaned_qs_data
|
|
311
|
+
|
|
312
|
+
# Once we have cleared any duplicates in the questionnaire we moving on to
|
|
313
|
+
# updating values according to the which field matches.
|
|
314
|
+
|
|
315
|
+
# Checking for matching PVs in questionnaire and archfile
|
|
316
|
+
# if the PV matches update the alias by removing the old key and making a new one
|
|
317
|
+
for (k, val) in sorted_qsDict.items():
|
|
318
|
+
# this looks up the key in the af Dictionary by finding the value
|
|
319
|
+
foundKey = get_key(val, sorted_afDict)
|
|
320
|
+
if k in sorted_afDict:
|
|
321
|
+
logger.debug("!Alias Match in questionnaire and archfile! Updating PV: "
|
|
322
|
+
+ k + ", " + sorted_qsDict[k])
|
|
323
|
+
sorted_afDict[k] = sorted_qsDict[k]
|
|
324
|
+
elif foundKey:
|
|
325
|
+
del sorted_afDict[foundKey]
|
|
326
|
+
sorted_afDict[k] = val
|
|
327
|
+
logger.debug("!PV Match in questionnaire and archfile! Updating Alias: "
|
|
328
|
+
+ k + ", " + val)
|
|
329
|
+
|
|
330
|
+
sorted_afDict = dict(sorted(sorted_afDict.items()))
|
|
331
|
+
updated_arch_list = [x for item in sorted_afDict.items() for x in item]
|
|
332
|
+
logger.debug("\nUpdated Arch List:\n")
|
|
333
|
+
logger.debug(updated_arch_list)
|
|
334
|
+
return updated_arch_list
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def read_archfile(exp_path):
|
|
338
|
+
if os.path.exists(exp_path):
|
|
339
|
+
with open(exp_path, "r") as experiment:
|
|
340
|
+
lines = experiment.readlines()
|
|
341
|
+
return lines
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def print_dry_run(exp_name):
|
|
345
|
+
"""
|
|
346
|
+
Print to stdout the data that would be stored in the epicsArch file.
|
|
347
|
+
|
|
348
|
+
Parameters
|
|
349
|
+
----------
|
|
350
|
+
exp_name : str
|
|
351
|
+
Experiment name, e.g.: xpplv6818
|
|
352
|
+
|
|
353
|
+
Examples
|
|
354
|
+
--------
|
|
355
|
+
>>> epicsarch-qs xpplv6818 --dry-run
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
qs_data = get_questionnaire_data(exp_name)
|
|
359
|
+
|
|
360
|
+
"""
|
|
361
|
+
Updating experiment file.
|
|
362
|
+
"""
|
|
363
|
+
|
|
364
|
+
af_path = EPICS_ARCH_FILE_PATH.format(exp_name[0:3]) + 'epicsArch_' + exp_name + '.txt'
|
|
365
|
+
af_data = read_archfile(af_path)
|
|
366
|
+
if not os.path.exists(af_path):
|
|
367
|
+
raise OSError('print_dry_run, invalid path: %s' % af_path)
|
|
368
|
+
elif os.path.exists(af_path):
|
|
369
|
+
updated_archFile = check_for_duplicates(qs_data, af_data)
|
|
370
|
+
|
|
371
|
+
for item in updated_archFile:
|
|
372
|
+
print(item)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def get_key(val, my_dict):
|
|
376
|
+
for k, v in my_dict.items():
|
|
377
|
+
if val == v:
|
|
378
|
+
return k
|
|
379
|
+
return None
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def get_questionnaire_data(exp_name):
|
|
383
|
+
"""
|
|
384
|
+
Return a list of items that would be written in the epicsArch file.
|
|
385
|
+
|
|
386
|
+
Parameters
|
|
387
|
+
----------
|
|
388
|
+
exp_name : str
|
|
389
|
+
Experiment name, e.g.: xpplv6818
|
|
390
|
+
|
|
391
|
+
Returns
|
|
392
|
+
-------
|
|
393
|
+
data_list : list
|
|
394
|
+
List containing the names ans prefixes of items in the Questionnaire.
|
|
395
|
+
"""
|
|
396
|
+
data_list = []
|
|
397
|
+
items = get_items(exp_name)
|
|
398
|
+
|
|
399
|
+
if items:
|
|
400
|
+
for item in items:
|
|
401
|
+
name = ''.join(('* ', item.name))
|
|
402
|
+
data_list.append(name)
|
|
403
|
+
data_list.append(item.prefix)
|
|
404
|
+
return data_list
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def get_items(exp_name):
|
|
408
|
+
"""
|
|
409
|
+
Get all_items from Questionnaire.
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
exp_name : str
|
|
414
|
+
Experiment name, e.g.: xpplv6818
|
|
415
|
+
|
|
416
|
+
Returns
|
|
417
|
+
-------
|
|
418
|
+
items : list
|
|
419
|
+
List of all_items from Questionnaire.
|
|
420
|
+
"""
|
|
421
|
+
try:
|
|
422
|
+
qs_client = get_qs_client(exp_name)
|
|
423
|
+
except Exception as ex:
|
|
424
|
+
logger.error('Failed to load the Questionnaire, %s', ex)
|
|
425
|
+
raise ex
|
|
426
|
+
sys.exit()
|
|
427
|
+
items = qs_client.all_items
|
|
428
|
+
if not items:
|
|
429
|
+
logger.warning("No devices found in PCDS Questionnaire for %s",
|
|
430
|
+
exp_name)
|
|
431
|
+
return
|
|
432
|
+
return items
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def update_file(exp_name, path):
|
|
436
|
+
"""
|
|
437
|
+
Create a file with aliases and pvs from the questionnaire.
|
|
438
|
+
|
|
439
|
+
Parameters
|
|
440
|
+
----------
|
|
441
|
+
exp_name : str
|
|
442
|
+
Experiment name, e.g.: `xpplv6818`
|
|
443
|
+
path : str
|
|
444
|
+
Directory where to create the epicsArch file.
|
|
445
|
+
"""
|
|
446
|
+
qs_data = get_questionnaire_data(exp_name)
|
|
447
|
+
|
|
448
|
+
path = str(path)
|
|
449
|
+
exp_name = str(exp_name)
|
|
450
|
+
|
|
451
|
+
logger.debug("UpdateFile: qs_data:\n" + str(qs_data))
|
|
452
|
+
logger.debug("\nPath: " + path)
|
|
453
|
+
|
|
454
|
+
af_path = path + "epicsArch_" + str(exp_name) + ".txt"
|
|
455
|
+
|
|
456
|
+
logger.debug("\nAF Path: " + af_path)
|
|
457
|
+
|
|
458
|
+
file_path = ''.join((path, 'epicsArch_', exp_name, '.txt'))
|
|
459
|
+
if not os.path.exists(path):
|
|
460
|
+
raise OSError('Invalid path: %s' % path)
|
|
461
|
+
# if the path exists but archfile does not, create af and pull qsd
|
|
462
|
+
elif os.path.exists(path) and not os.path.exists(af_path):
|
|
463
|
+
logger.debug("UpdateFile: Path is valid, creating archfile\n")
|
|
464
|
+
logger.debug('Creating epicsArch file for experiment: %s', exp_name)
|
|
465
|
+
cleaned_data = check_for_duplicates(qs_data, {})
|
|
466
|
+
|
|
467
|
+
# if the path and archfile exists, update af and pull
|
|
468
|
+
elif os.path.exists(path) and os.path.exists(af_path):
|
|
469
|
+
logger.debug("UpdateFile: Path exists and archfile exists\n")
|
|
470
|
+
af_data = read_archfile(af_path)
|
|
471
|
+
cleaned_data = check_for_duplicates(qs_data, af_data)
|
|
472
|
+
|
|
473
|
+
# Write updates to the corresponding file
|
|
474
|
+
with open(file_path, 'w') as f:
|
|
475
|
+
for data in cleaned_data:
|
|
476
|
+
try:
|
|
477
|
+
f.write(f'{data}\n')
|
|
478
|
+
except OSError as ex:
|
|
479
|
+
logger.error('Could not write file %s, %s', file_path, ex)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
if __name__ == '__main__':
|
|
483
|
+
main()
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import enum
|
|
1
2
|
import inspect
|
|
2
3
|
import logging
|
|
3
4
|
|
|
@@ -17,10 +18,18 @@ except ImportError:
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
class DeviceLoadLevel(enum.IntEnum):
|
|
22
|
+
# HUTCH = 0 # TODO: Figure out how to gather only hutch devices
|
|
23
|
+
UPSTREAM = 1
|
|
24
|
+
STANDARD = 2
|
|
25
|
+
ALL = 3
|
|
26
|
+
|
|
27
|
+
|
|
20
28
|
def get_happi_objs(
|
|
21
29
|
db: str,
|
|
22
30
|
light_ctrl: LightController,
|
|
23
31
|
endstation: str,
|
|
32
|
+
load_level: DeviceLoadLevel = DeviceLoadLevel.STANDARD
|
|
24
33
|
) -> dict[str, ophyd.Device]:
|
|
25
34
|
"""
|
|
26
35
|
Get the relevant items for ``endstation`` from the happi database ``db``.
|
|
@@ -55,6 +64,11 @@ def get_happi_objs(
|
|
|
55
64
|
client = happi.Client(path=db)
|
|
56
65
|
containers = list()
|
|
57
66
|
|
|
67
|
+
if load_level == DeviceLoadLevel.ALL:
|
|
68
|
+
results = client.search(active=True)
|
|
69
|
+
containers.extend(res.item for res in results)
|
|
70
|
+
return _load_devices(*containers)
|
|
71
|
+
|
|
58
72
|
if light_ctrl is None or (endstation.upper() not in light_ctrl.beamlines):
|
|
59
73
|
# lightpath was unavailable, search by beamline name
|
|
60
74
|
reqs = dict(beamline=endstation.upper(), active=True)
|
|
@@ -73,21 +87,22 @@ def get_happi_objs(
|
|
|
73
87
|
results = client.search(name=name)
|
|
74
88
|
containers.extend(res.item for res in results)
|
|
75
89
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
if load_level >= DeviceLoadLevel.STANDARD:
|
|
91
|
+
# also any device with the same beamline name
|
|
92
|
+
# since lightpath only grabs lightpath-active devices
|
|
93
|
+
beamlines = {it.beamline for it in containers}
|
|
94
|
+
beamlines.add(endstation.upper())
|
|
95
|
+
|
|
96
|
+
for line in beamlines:
|
|
97
|
+
# Assume we want hutch items that are active
|
|
98
|
+
# items can be lightpath-inactive
|
|
99
|
+
reqs = dict(beamline=line, active=True)
|
|
100
|
+
results = client.search(**reqs)
|
|
101
|
+
blc = [res.item for res in results
|
|
102
|
+
if res.item.name not in dev_names]
|
|
103
|
+
# Add the beamline containers to the complete list
|
|
104
|
+
if blc:
|
|
105
|
+
containers.extend(blc)
|
|
91
106
|
|
|
92
107
|
if len(containers) < 1:
|
|
93
108
|
logger.warning(f'{len(containers)} active devices found for '
|