From 01d880a416897f2917b8500c3fe03dd2fb205584 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Thu, 6 Mar 2025 12:52:49 -0600 Subject: [PATCH 01/38] set up is in 2 steps so RE no longer crashes stuff --- README.md | 14 ++++---------- src/instrument/startup.py | 5 +---- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 71d5a3e..134ffcb 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ conda activate $ENV_NAME pip install -e ."[all]" ``` -## IPython console +## IPython console & Jupyter notebook To start the bluesky instrument session in a ipython execute the next command in a terminal: @@ -31,20 +31,14 @@ To start the bluesky instrument session in a ipython execute the next command in ipython ``` -Inside the ipython console execute: - -```py -from instrument.startup import * -``` - -## Jupyter notebook - +## Jupyter Notebook Start JupyterLab, a Jupyter notebook server, or a notebook, VSCode. -Start the data acquisition: +## Starting the Aquisition ```py from instrument.startup import * +RE(make_devices()) # create all the ophyd-style control devices ``` ## Sim Plan Demo diff --git a/src/instrument/startup.py b/src/instrument/startup.py index e780604..3051da8 100644 --- a/src/instrument/startup.py +++ b/src/instrument/startup.py @@ -23,10 +23,9 @@ from .utils.config_loaders import iconfig from .utils.helper_functions import register_bluesky_magics from .utils.helper_functions import running_in_queueserver -from .utils.make_devices_yaml import make_devices logger = logging.getLogger(__name__) -logger.bsdev(__file__) + if iconfig.get("USE_BLUESKY_MAGICS", False): register_bluesky_magics() @@ -56,5 +55,3 @@ from bluesky import plans as bp # noqa: F401 from .utils.controls_setup import oregistry # noqa: F401 - -RE(make_devices()) # create all the ophyd-style control devices From 1a0c9e95917f4c587a7dff20c18a2b4bc1032fc2 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Thu, 6 Mar 2025 14:50:25 -0600 Subject: [PATCH 02/38] fixed issue with error logging magics --- src/instrument/utils/logging_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/instrument/utils/logging_setup.py b/src/instrument/utils/logging_setup.py index 3873be0..22f7f4b 100644 --- a/src/instrument/utils/logging_setup.py +++ b/src/instrument/utils/logging_setup.py @@ -193,7 +193,7 @@ def _setup_ipython_logger(logger, cfg): "\nBelow are the IPython logging settings for your session." "\nThese settings have no impact on your experiment.\n" ) - _ipython.magic(f"logstart {options} {log_file} {log_mode}") + _ipython.run_line_magic("logstart", f"{options} {log_file} {log_mode}") if logger is not None: logger.bsdev("Console logging: %s", log_file) except Exception as exc: From fd9eb39227cb9f1379eba93125c9f4c8ca48437e Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Thu, 6 Mar 2025 14:55:51 -0600 Subject: [PATCH 03/38] make devices works no error --- README.md | 2 +- src/instrument/configs/iconfig.yml | 15 ++++++++------- src/instrument/startup.py | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 134ffcb..354f5d3 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ ipython ## Jupyter Notebook Start JupyterLab, a Jupyter notebook server, or a notebook, VSCode. -## Starting the Aquisition +## Starting the BITS Package ```py from instrument.startup import * diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index fb50aca..cc33ace 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -27,19 +27,19 @@ RUN_ENGINE: ### The progress bar is nice to see, ### except when it clutters the output in Jupyter notebooks. - ### Default: True + ### Default: False USE_PROGRESS_BAR: false # Command-line tools, such as %wa, %ct, ... -USE_BLUESKY_MAGICS: True +USE_BLUESKY_MAGICS: true ### Best Effort Callback Configurations ### Defaults: all true (except no plots in queueserver) -# BEC: -# BASELINE: false -# HEADING: false -# PLOTS: false -# TABLE: false +BEC: + BASELINE: true + HEADING: true + PLOTS: true + TABLE: true ### Support for known output file formats. ### Uncomment to use. If undefined, will not write that type of file. @@ -47,6 +47,7 @@ USE_BLUESKY_MAGICS: True # NEXUS_DATA_FILES: # FILE_EXTENSION: hdf # WARN_MISSING_CONTENT: true + SPEC_DATA_FILES: FILE_EXTENSION: dat diff --git a/src/instrument/startup.py b/src/instrument/startup.py index 3051da8..1cc69d8 100644 --- a/src/instrument/startup.py +++ b/src/instrument/startup.py @@ -16,16 +16,16 @@ from .core.catalog_init import cat # noqa: F401 from .core.run_engine_init import RE # noqa: F401 from .core.run_engine_init import sd # noqa: F401 -from .devices import * # noqa: F403 from .plans import * # noqa: F403 # Bluesky data acquisition setup from .utils.config_loaders import iconfig from .utils.helper_functions import register_bluesky_magics from .utils.helper_functions import running_in_queueserver +from .utils.make_devices_yaml import make_devices # noqa: F401 logger = logging.getLogger(__name__) - +logger.bsdev(__file__) if iconfig.get("USE_BLUESKY_MAGICS", False): register_bluesky_magics() From 107f5c1e97f0feca01dfa990f8e516d7c9bac8fa Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Fri, 7 Mar 2025 13:57:09 -0600 Subject: [PATCH 04/38] organizing factory device --- README.md | 2 +- src/instrument/configs/devices.yml | 2 +- src/instrument/tests/test_device_factories.py | 4 ++-- src/instrument/{devices/factories.py => utils/sim_creator.py} | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename src/instrument/{devices/factories.py => utils/sim_creator.py} (100%) diff --git a/README.md b/README.md index 354f5d3..5cf4532 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ from instrument.startup import * RE(make_devices()) # create all the ophyd-style control devices ``` -## Sim Plan Demo +## Running Sim Plan Demo To run some simulated plans that ensure the installation worked as expected please run the next commands inside an ipython session or a jupyter notebook diff --git a/src/instrument/configs/devices.yml b/src/instrument/configs/devices.yml index b9c12be..8b85247 100644 --- a/src/instrument/configs/devices.yml +++ b/src/instrument/configs/devices.yml @@ -1,6 +1,6 @@ # Guarneri-style device YAML configuration -instrument.devices.factories.predefined_device: +instrument.utils.sim_creator.predefined_device: - {creator: ophyd.sim.motor, name: sim_motor} - {creator: ophyd.sim.noisy_det, name: sim_det} diff --git a/src/instrument/tests/test_device_factories.py b/src/instrument/tests/test_device_factories.py index 39588a3..ec0a093 100644 --- a/src/instrument/tests/test_device_factories.py +++ b/src/instrument/tests/test_device_factories.py @@ -2,8 +2,8 @@ import pytest -from ..devices.factories import motors -from ..devices.factories import predefined_device +from ..utils.factories import motors +from ..utils.factories import predefined_device @pytest.mark.parametrize( diff --git a/src/instrument/devices/factories.py b/src/instrument/utils/sim_creator.py similarity index 100% rename from src/instrument/devices/factories.py rename to src/instrument/utils/sim_creator.py From eff48dd5e5b43aaaf95a689937d663de15172b75 Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Fri, 7 Mar 2025 18:55:15 -0600 Subject: [PATCH 05/38] fix tests --- src/instrument/tests/test_device_factories.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/instrument/tests/test_device_factories.py b/src/instrument/tests/test_device_factories.py index ec0a093..1adc844 100644 --- a/src/instrument/tests/test_device_factories.py +++ b/src/instrument/tests/test_device_factories.py @@ -2,8 +2,8 @@ import pytest -from ..utils.factories import motors -from ..utils.factories import predefined_device +from ..utils.sim_creator import motors +from ..utils.sim_creator import predefined_device @pytest.mark.parametrize( From 4c54b5ccac6d6d534dbe14a56dd96038b358ec42 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Sat, 8 Mar 2025 10:28:27 -0600 Subject: [PATCH 06/38] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5cf4532..7c82c80 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ conda activate $ENV_NAME pip install -e ."[all]" ``` -## IPython console & Jupyter notebook +## IPython console To start the bluesky instrument session in a ipython execute the next command in a terminal: From 9fd17443719edd9e25753d77da72e6c34bd66b20 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Mon, 10 Mar 2025 15:29:51 -0500 Subject: [PATCH 07/38] added features to iconfig --- src/instrument/callbacks/nexus_data_file_writer.py | 2 +- src/instrument/configs/iconfig.yml | 13 ++++++++----- src/instrument/startup.py | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/instrument/callbacks/nexus_data_file_writer.py b/src/instrument/callbacks/nexus_data_file_writer.py index 1f06ede..2e543de 100644 --- a/src/instrument/callbacks/nexus_data_file_writer.py +++ b/src/instrument/callbacks/nexus_data_file_writer.py @@ -48,7 +48,7 @@ def get_sample_title(self): nxwriter = MyNXWriter() # create the callback instance """The NeXus file writer object.""" -if "NEXUS_DATA_FILES" in iconfig: +if iconfig.get("NEXUS_DATA_FILES", {}).get("IS_ON", False): RE.subscribe(nxwriter.receiver) # write data to NeXus files nxwriter.file_extension = iconfig.get("FILE_EXTENSION", "hdf") diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index cc33ace..f16d1ca 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -44,11 +44,13 @@ BEC: ### Support for known output file formats. ### Uncomment to use. If undefined, will not write that type of file. ### Each callback should apply its configuration from here. -# NEXUS_DATA_FILES: -# FILE_EXTENSION: hdf -# WARN_MISSING_CONTENT: true +NEXUS_DATA_FILES: + IS_ON: false + FILE_EXTENSION: hdf + WARN_MISSING_CONTENT: true SPEC_DATA_FILES: + IS_ON: true FILE_EXTENSION: dat ### APS Data Management @@ -65,7 +67,7 @@ OPHYD: ### Control layer for ophyd to communicate with EPICS. ### Default: PyEpics ### Choices: "PyEpics" or "caproto" - # CONTROL_LAYER: caproto + CONTROL_LAYER: PyEpics ### default timeouts (seconds) TIMEOUTS: @@ -74,4 +76,5 @@ OPHYD: PV_CONNECTION: *TIMEOUT # Control detail of exception traces in IPython (console and notebook). -XMODE_DEBUG_LEVEL: Minimal +# Options are: Minimal, Plain, Verbose +XMODE_DEBUG_LEVEL: Minimal \ No newline at end of file diff --git a/src/instrument/startup.py b/src/instrument/startup.py index 1cc69d8..e32325c 100644 --- a/src/instrument/startup.py +++ b/src/instrument/startup.py @@ -31,10 +31,10 @@ register_bluesky_magics() # Configure the session with callbacks, devices, and plans. -if iconfig.get("NEXUS_DATA_FILES") is not None: +if iconfig.get("NEXUS_DATA_FILES", {}).get("IS_ON", False): from .callbacks.nexus_data_file_writer import nxwriter # noqa: F401 -if iconfig.get("SPEC_DATA_FILES") is not None: +if iconfig.get("SPEC_DATA_FILES", {}).get("IS_ON", False): from .callbacks.spec_data_file_writer import newSpecFile # noqa: F401 from .callbacks.spec_data_file_writer import spec_comment # noqa: F401 from .callbacks.spec_data_file_writer import specwriter # noqa: F401 From dfbf7f27ee893c30dec54ff5d19e478a3ab2cbce Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Mon, 10 Mar 2025 16:32:07 -0500 Subject: [PATCH 08/38] rebase --- README.md | 4 ++-- src/instrument/startup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1613387..5728920 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ conda activate $ENV_NAME pip install -e ."[all]" ``` -## IPython console +## IPython console & Jupyter notebook To start the bluesky instrument session in a ipython execute the next command in a terminal: @@ -34,7 +34,7 @@ ipython ## Jupyter Notebook Start JupyterLab, a Jupyter notebook server, or a notebook, VSCode. -## Starting the BITS Package +## Starting the Aquisition ```py from instrument.startup import * diff --git a/src/instrument/startup.py b/src/instrument/startup.py index e32325c..30e2af9 100644 --- a/src/instrument/startup.py +++ b/src/instrument/startup.py @@ -25,7 +25,7 @@ from .utils.make_devices_yaml import make_devices # noqa: F401 logger = logging.getLogger(__name__) -logger.bsdev(__file__) + if iconfig.get("USE_BLUESKY_MAGICS", False): register_bluesky_magics() From ab38d00ff697364b05e146f5fd94a6e0d85e3119 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Thu, 6 Mar 2025 14:55:51 -0600 Subject: [PATCH 09/38] make devices works no error --- README.md | 2 +- src/instrument/configs/iconfig.yml | 6 ++++++ src/instrument/startup.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5728920..e17dbf9 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ ipython ## Jupyter Notebook Start JupyterLab, a Jupyter notebook server, or a notebook, VSCode. -## Starting the Aquisition +## Starting the BITS Package ```py from instrument.startup import * diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index f16d1ca..2bf0605 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -44,10 +44,16 @@ BEC: ### Support for known output file formats. ### Uncomment to use. If undefined, will not write that type of file. ### Each callback should apply its configuration from here. +<<<<<<< HEAD NEXUS_DATA_FILES: IS_ON: false FILE_EXTENSION: hdf WARN_MISSING_CONTENT: true +======= +# NEXUS_DATA_FILES: +# FILE_EXTENSION: hdf +# WARN_MISSING_CONTENT: true +>>>>>>> 89490d3 (make devices works no error) SPEC_DATA_FILES: IS_ON: true diff --git a/src/instrument/startup.py b/src/instrument/startup.py index 30e2af9..e32325c 100644 --- a/src/instrument/startup.py +++ b/src/instrument/startup.py @@ -25,7 +25,7 @@ from .utils.make_devices_yaml import make_devices # noqa: F401 logger = logging.getLogger(__name__) - +logger.bsdev(__file__) if iconfig.get("USE_BLUESKY_MAGICS", False): register_bluesky_magics() From e125b663e4748c3ce1556a08f8ee8ce9ff54a913 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Mon, 10 Mar 2025 16:33:08 -0500 Subject: [PATCH 10/38] rebase --- docs/source/guides/index.rst | 2 +- docs/source/guides/template_sync.rst | 15 ++++++++------- src/instrument/configs/iconfig.yml | 6 ------ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/source/guides/index.rst b/docs/source/guides/index.rst index 34d6d5e..7846a67 100644 --- a/docs/source/guides/index.rst +++ b/docs/source/guides/index.rst @@ -9,5 +9,5 @@ Guides show how to use certain features of this instrument. :maxdepth: 2 :glob: - *dm* + dm template_sync diff --git a/docs/source/guides/template_sync.rst b/docs/source/guides/template_sync.rst index e074c39..7f4a9dc 100644 --- a/docs/source/guides/template_sync.rst +++ b/docs/source/guides/template_sync.rst @@ -15,7 +15,8 @@ Overview -------- This document describes a method to synchronize the new instrument repo -with the template repo. The method relies on a GitHub workflow +with the template repo. The method relies on a GitHub +`workflow `__ to generate a new pull request whenever the template is updated. The workflow can be run on demand or as a periodic task (default is once a month). The workflow is installed in the new instrument's @@ -25,16 +26,16 @@ When the workflow is run, it compares the new instrument's repo with the template repo. If differences are identified, the workflow creates a new branch in the new instrument's repo and then creates a new pull request to merge that branch with ``main``. Additional configuration is -necessary to grant permission for the workflow to create a branch and PR. +necessary to grant permission for the workflow to create a branch and +PR. -Permission is provided through a GitHub Personal Access Token (PAT). [#]_ -For this purpose, the PAT settings [#settings]_ should allow ``write`` permission +Permission is provided through a GitHub Personal Access Token +(`PAT `__). +For this purpose, the PAT +`settings `__ should allow ``write`` (includes ``read``) permission for ``workflow``. It is possible to limit a PAT to a single GitHub repo (a good idea for this use case). -.. [#] https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens -.. [#settings] https://github.com/settings/tokens - Example instrument repository +++++++++++++++++++++++++++++ diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index 2bf0605..f16d1ca 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -44,16 +44,10 @@ BEC: ### Support for known output file formats. ### Uncomment to use. If undefined, will not write that type of file. ### Each callback should apply its configuration from here. -<<<<<<< HEAD NEXUS_DATA_FILES: IS_ON: false FILE_EXTENSION: hdf WARN_MISSING_CONTENT: true -======= -# NEXUS_DATA_FILES: -# FILE_EXTENSION: hdf -# WARN_MISSING_CONTENT: true ->>>>>>> 89490d3 (make devices works no error) SPEC_DATA_FILES: IS_ON: true From 232c5425a8119a44296a0d55ff93e86007fcc1ec Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Mon, 10 Feb 2025 13:31:39 -0600 Subject: [PATCH 11/38] MNT #37 following repo rename --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e17dbf9..b74b583 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ cd BITS Set up the development environment. - -export ENV_NAME=bits +```bash +export ENV_NAME=BITS_env conda create -y -n $ENV_NAME python=3.11 pyepics conda activate $ENV_NAME pip install -e ."[all]" From 8e584eebea5697be4aa9b862fbda2aaeaf25f1cf Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Fri, 7 Mar 2025 18:42:21 -0600 Subject: [PATCH 12/38] always enabled --- .github/workflows/template-sync.yml | 35 ----------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/template-sync.yml diff --git a/.github/workflows/template-sync.yml b/.github/workflows/template-sync.yml deleted file mode 100644 index 25ba77b..0000000 --- a/.github/workflows/template-sync.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: template_sync - -# see: https://github.com/AndreasAugustin/actions-template-sync - -on: - # cronjob trigger - schedule: - # 12:10 AM GMT on the first day of every month - - cron: "10 0 1 * *" - # manual trigger - workflow_dispatch: - -env: - TEMPLATE_REPO: BCDA-APS/BITS - -jobs: - repo-sync: - if: ${{ github.repository != 'bcda-aps/bits' }} - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - - steps: - # To use this repository's private action, you must check out the repository - - name: Checkout - uses: actions/checkout@v4 - with: - token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT - - - name: actions-template-sync - uses: AndreasAugustin/actions-template-sync@v2 - with: - source_repo_path: ${{ env.TEMPLATE_REPO }} - upstream_branch: main From 2b8789c4dcb7229f8dfcfe2aacd4bdb31b4d2e1b Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 17:13:51 -0500 Subject: [PATCH 13/38] new instrument --- pyproject.toml | 4 +- src/{instrument => bits}/__init__.py | 0 .../callbacks/__init__.py | 0 .../callbacks/nexus_data_file_writer.py | 0 .../callbacks/spec_data_file_writer.py | 0 src/{instrument => bits}/core/__init__.py | 0 .../core/best_effort_init.py | 0 src/{instrument => bits}/core/catalog_init.py | 0 .../core/run_engine_init.py | 0 .../demo_instrument}/__init__.py | 0 .../demo_instrument}/configs/__init__.py | 0 .../demo_instrument}/configs/devices.yml | 0 .../configs/devices_aps_only.yml | 0 .../demo_instrument}/configs/iconfig.yml | 0 .../demo_instrument}/configs/logging.yml | 0 .../demo_instrument}/devices/__init__.py | 0 .../demo_instrument}/plans/__init__.py | 0 .../demo_instrument}/plans/dm_plans.py | 0 .../demo_instrument}/plans/sim_plans.py | 2 +- .../demo_instrument}/startup.py | 33 ++-- src/{instrument => bits}/tests/__init__.py | 0 src/{instrument => bits}/tests/conftest.py | 4 +- .../tests/test_device_factories.py | 4 +- .../tests/test_general.py | 20 +-- .../tests/test_stored_dict.py | 4 +- src/{instrument => bits}/utils/__init__.py | 0 .../utils/aps_functions.py | 0 .../utils/config_loaders.py | 0 .../utils/controls_setup.py | 0 src/bits/utils/create_new_instrument.py | 160 ++++++++++++++++++ .../utils/helper_functions.py | 0 .../utils/logging_setup.py | 0 .../utils/make_devices_yaml.py | 8 +- src/{instrument => bits}/utils/metadata.py | 0 src/{instrument => bits}/utils/sim_creator.py | 0 src/{instrument => bits}/utils/stored_dict.py | 0 36 files changed, 201 insertions(+), 38 deletions(-) rename src/{instrument => bits}/__init__.py (100%) rename src/{instrument => bits}/callbacks/__init__.py (100%) rename src/{instrument => bits}/callbacks/nexus_data_file_writer.py (100%) rename src/{instrument => bits}/callbacks/spec_data_file_writer.py (100%) rename src/{instrument => bits}/core/__init__.py (100%) rename src/{instrument => bits}/core/best_effort_init.py (100%) rename src/{instrument => bits}/core/catalog_init.py (100%) rename src/{instrument => bits}/core/run_engine_init.py (100%) rename src/{instrument/beamline => bits/demo_instrument}/__init__.py (100%) rename src/{instrument => bits/demo_instrument}/configs/__init__.py (100%) rename src/{instrument => bits/demo_instrument}/configs/devices.yml (100%) rename src/{instrument => bits/demo_instrument}/configs/devices_aps_only.yml (100%) rename src/{instrument => bits/demo_instrument}/configs/iconfig.yml (100%) rename src/{instrument => bits/demo_instrument}/configs/logging.yml (100%) rename src/{instrument => bits/demo_instrument}/devices/__init__.py (100%) rename src/{instrument => bits/demo_instrument}/plans/__init__.py (100%) rename src/{instrument => bits/demo_instrument}/plans/dm_plans.py (100%) rename src/{instrument => bits/demo_instrument}/plans/sim_plans.py (97%) rename src/{instrument => bits/demo_instrument}/startup.py (58%) rename src/{instrument => bits}/tests/__init__.py (100%) rename src/{instrument => bits}/tests/conftest.py (88%) rename src/{instrument => bits}/tests/test_device_factories.py (93%) rename src/{instrument => bits}/tests/test_general.py (78%) rename src/{instrument => bits}/tests/test_stored_dict.py (97%) rename src/{instrument => bits}/utils/__init__.py (100%) rename src/{instrument => bits}/utils/aps_functions.py (100%) rename src/{instrument => bits}/utils/config_loaders.py (100%) rename src/{instrument => bits}/utils/controls_setup.py (100%) create mode 100644 src/bits/utils/create_new_instrument.py rename src/{instrument => bits}/utils/helper_functions.py (100%) rename src/{instrument => bits}/utils/logging_setup.py (100%) rename src/{instrument => bits}/utils/make_devices_yaml.py (93%) rename src/{instrument => bits}/utils/metadata.py (100%) rename src/{instrument => bits}/utils/sim_creator.py (100%) rename src/{instrument => bits}/utils/stored_dict.py (100%) diff --git a/pyproject.toml b/pyproject.toml index 91c0623..7822016 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -221,10 +221,10 @@ skip-magic-trailing-comma = false line-ending = "auto" [tool.setuptools] -package-dir = {"instrument" = "src/instrument"} +package-dir = {"bits" = "src/bits"} [tool.setuptools.package-data] "*" = ["*.yml"] [tool.setuptools_scm] -write_to = "src/instrument/_version.py" +write_to = "src/bits/_version.py" diff --git a/src/instrument/__init__.py b/src/bits/__init__.py similarity index 100% rename from src/instrument/__init__.py rename to src/bits/__init__.py diff --git a/src/instrument/callbacks/__init__.py b/src/bits/callbacks/__init__.py similarity index 100% rename from src/instrument/callbacks/__init__.py rename to src/bits/callbacks/__init__.py diff --git a/src/instrument/callbacks/nexus_data_file_writer.py b/src/bits/callbacks/nexus_data_file_writer.py similarity index 100% rename from src/instrument/callbacks/nexus_data_file_writer.py rename to src/bits/callbacks/nexus_data_file_writer.py diff --git a/src/instrument/callbacks/spec_data_file_writer.py b/src/bits/callbacks/spec_data_file_writer.py similarity index 100% rename from src/instrument/callbacks/spec_data_file_writer.py rename to src/bits/callbacks/spec_data_file_writer.py diff --git a/src/instrument/core/__init__.py b/src/bits/core/__init__.py similarity index 100% rename from src/instrument/core/__init__.py rename to src/bits/core/__init__.py diff --git a/src/instrument/core/best_effort_init.py b/src/bits/core/best_effort_init.py similarity index 100% rename from src/instrument/core/best_effort_init.py rename to src/bits/core/best_effort_init.py diff --git a/src/instrument/core/catalog_init.py b/src/bits/core/catalog_init.py similarity index 100% rename from src/instrument/core/catalog_init.py rename to src/bits/core/catalog_init.py diff --git a/src/instrument/core/run_engine_init.py b/src/bits/core/run_engine_init.py similarity index 100% rename from src/instrument/core/run_engine_init.py rename to src/bits/core/run_engine_init.py diff --git a/src/instrument/beamline/__init__.py b/src/bits/demo_instrument/__init__.py similarity index 100% rename from src/instrument/beamline/__init__.py rename to src/bits/demo_instrument/__init__.py diff --git a/src/instrument/configs/__init__.py b/src/bits/demo_instrument/configs/__init__.py similarity index 100% rename from src/instrument/configs/__init__.py rename to src/bits/demo_instrument/configs/__init__.py diff --git a/src/instrument/configs/devices.yml b/src/bits/demo_instrument/configs/devices.yml similarity index 100% rename from src/instrument/configs/devices.yml rename to src/bits/demo_instrument/configs/devices.yml diff --git a/src/instrument/configs/devices_aps_only.yml b/src/bits/demo_instrument/configs/devices_aps_only.yml similarity index 100% rename from src/instrument/configs/devices_aps_only.yml rename to src/bits/demo_instrument/configs/devices_aps_only.yml diff --git a/src/instrument/configs/iconfig.yml b/src/bits/demo_instrument/configs/iconfig.yml similarity index 100% rename from src/instrument/configs/iconfig.yml rename to src/bits/demo_instrument/configs/iconfig.yml diff --git a/src/instrument/configs/logging.yml b/src/bits/demo_instrument/configs/logging.yml similarity index 100% rename from src/instrument/configs/logging.yml rename to src/bits/demo_instrument/configs/logging.yml diff --git a/src/instrument/devices/__init__.py b/src/bits/demo_instrument/devices/__init__.py similarity index 100% rename from src/instrument/devices/__init__.py rename to src/bits/demo_instrument/devices/__init__.py diff --git a/src/instrument/plans/__init__.py b/src/bits/demo_instrument/plans/__init__.py similarity index 100% rename from src/instrument/plans/__init__.py rename to src/bits/demo_instrument/plans/__init__.py diff --git a/src/instrument/plans/dm_plans.py b/src/bits/demo_instrument/plans/dm_plans.py similarity index 100% rename from src/instrument/plans/dm_plans.py rename to src/bits/demo_instrument/plans/dm_plans.py diff --git a/src/instrument/plans/sim_plans.py b/src/bits/demo_instrument/plans/sim_plans.py similarity index 97% rename from src/instrument/plans/sim_plans.py rename to src/bits/demo_instrument/plans/sim_plans.py index 424269d..ef5bd18 100644 --- a/src/instrument/plans/sim_plans.py +++ b/src/bits/demo_instrument/plans/sim_plans.py @@ -15,7 +15,7 @@ from bluesky import plan_stubs as bps from bluesky import plans as bp -from ..utils.controls_setup import oregistry +from bits.utils.controls_setup import oregistry logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/instrument/startup.py b/src/bits/demo_instrument/startup.py similarity index 58% rename from src/instrument/startup.py rename to src/bits/demo_instrument/startup.py index e32325c..a444c2c 100644 --- a/src/instrument/startup.py +++ b/src/bits/demo_instrument/startup.py @@ -11,18 +11,21 @@ import logging -from .core.best_effort_init import bec # noqa: F401 -from .core.best_effort_init import peaks # noqa: F401 -from .core.catalog_init import cat # noqa: F401 -from .core.run_engine_init import RE # noqa: F401 -from .core.run_engine_init import sd # noqa: F401 -from .plans import * # noqa: F403 +from bits.core.best_effort_init import bec # noqa: F401 +from bits.core.best_effort_init import peaks # noqa: F401 +from bits.core.catalog_init import cat # noqa: F401 +from bits.core.run_engine_init import RE # noqa: F401 +from bits.core.run_engine_init import sd # noqa: F401 # Bluesky data acquisition setup -from .utils.config_loaders import iconfig -from .utils.helper_functions import register_bluesky_magics -from .utils.helper_functions import running_in_queueserver -from .utils.make_devices_yaml import make_devices # noqa: F401 +from bits.utils.config_loaders import iconfig +from bits.utils.helper_functions import register_bluesky_magics +from bits.utils.helper_functions import running_in_queueserver +from bits.utils.make_devices_yaml import make_devices # noqa: F401 + +# User specific imports +from .plans import * # noqa: F403 + logger = logging.getLogger(__name__) logger.bsdev(__file__) @@ -32,12 +35,12 @@ # Configure the session with callbacks, devices, and plans. if iconfig.get("NEXUS_DATA_FILES", {}).get("IS_ON", False): - from .callbacks.nexus_data_file_writer import nxwriter # noqa: F401 + from ..callbacks.nexus_data_file_writer import nxwriter # noqa: F401 if iconfig.get("SPEC_DATA_FILES", {}).get("IS_ON", False): - from .callbacks.spec_data_file_writer import newSpecFile # noqa: F401 - from .callbacks.spec_data_file_writer import spec_comment # noqa: F401 - from .callbacks.spec_data_file_writer import specwriter # noqa: F401 + from ..callbacks.spec_data_file_writer import newSpecFile # noqa: F401 + from ..callbacks.spec_data_file_writer import spec_comment # noqa: F401 + from ..callbacks.spec_data_file_writer import specwriter # noqa: F401 # These imports must come after the above setup. if running_in_queueserver(): @@ -54,4 +57,4 @@ from bluesky import plan_stubs as bps # noqa: F401 from bluesky import plans as bp # noqa: F401 - from .utils.controls_setup import oregistry # noqa: F401 + from ..utils.controls_setup import oregistry # noqa: F401 diff --git a/src/instrument/tests/__init__.py b/src/bits/tests/__init__.py similarity index 100% rename from src/instrument/tests/__init__.py rename to src/bits/tests/__init__.py diff --git a/src/instrument/tests/conftest.py b/src/bits/tests/conftest.py similarity index 88% rename from src/instrument/tests/conftest.py rename to src/bits/tests/conftest.py index db19437..7dc5a5c 100644 --- a/src/instrument/tests/conftest.py +++ b/src/bits/tests/conftest.py @@ -13,8 +13,8 @@ import pytest -from ..startup import RE -from ..startup import make_devices +from bits.demo_instrument.startup import RE +from bits.demo_instrument.startup import make_devices @pytest.fixture(scope="session") diff --git a/src/instrument/tests/test_device_factories.py b/src/bits/tests/test_device_factories.py similarity index 93% rename from src/instrument/tests/test_device_factories.py rename to src/bits/tests/test_device_factories.py index 1adc844..a350297 100644 --- a/src/instrument/tests/test_device_factories.py +++ b/src/bits/tests/test_device_factories.py @@ -2,8 +2,8 @@ import pytest -from ..utils.sim_creator import motors -from ..utils.sim_creator import predefined_device +from bits.utils.sim_creator import motors +from bits.utils.sim_creator import predefined_device @pytest.mark.parametrize( diff --git a/src/instrument/tests/test_general.py b/src/bits/tests/test_general.py similarity index 78% rename from src/instrument/tests/test_general.py rename to src/bits/tests/test_general.py index c808c3c..09a0827 100644 --- a/src/instrument/tests/test_general.py +++ b/src/bits/tests/test_general.py @@ -6,16 +6,16 @@ import pytest -from ..plans.sim_plans import sim_count_plan -from ..plans.sim_plans import sim_print_plan -from ..plans.sim_plans import sim_rel_scan_plan -from ..startup import bec -from ..startup import cat -from ..startup import iconfig -from ..startup import peaks -from ..startup import running_in_queueserver -from ..startup import sd -from ..startup import specwriter +from bits.demo_instrument.plans.sim_plans import sim_count_plan +from bits.demo_instrument.plans.sim_plans import sim_print_plan +from bits.demo_instrument.plans.sim_plans import sim_rel_scan_plan +from bits.demo_instrument.startup import bec +from bits.demo_instrument.startup import cat +from bits.demo_instrument.startup import iconfig +from bits.demo_instrument.startup import peaks +from bits.demo_instrument.startup import running_in_queueserver +from bits.demo_instrument.startup import sd +from bits.demo_instrument.startup import specwriter def test_startup(runengine_with_devices: object) -> None: diff --git a/src/instrument/tests/test_stored_dict.py b/src/bits/tests/test_stored_dict.py similarity index 97% rename from src/instrument/tests/test_stored_dict.py rename to src/bits/tests/test_stored_dict.py index 9972240..03b6191 100644 --- a/src/instrument/tests/test_stored_dict.py +++ b/src/bits/tests/test_stored_dict.py @@ -9,8 +9,8 @@ import pytest -from ..utils.config_loaders import load_config_yaml -from ..utils.stored_dict import StoredDict +from bits.utils.config_loaders import load_config_yaml +from bits.utils.stored_dict import StoredDict def luftpause(delay=0.05): diff --git a/src/instrument/utils/__init__.py b/src/bits/utils/__init__.py similarity index 100% rename from src/instrument/utils/__init__.py rename to src/bits/utils/__init__.py diff --git a/src/instrument/utils/aps_functions.py b/src/bits/utils/aps_functions.py similarity index 100% rename from src/instrument/utils/aps_functions.py rename to src/bits/utils/aps_functions.py diff --git a/src/instrument/utils/config_loaders.py b/src/bits/utils/config_loaders.py similarity index 100% rename from src/instrument/utils/config_loaders.py rename to src/bits/utils/config_loaders.py diff --git a/src/instrument/utils/controls_setup.py b/src/bits/utils/controls_setup.py similarity index 100% rename from src/instrument/utils/controls_setup.py rename to src/bits/utils/controls_setup.py diff --git a/src/bits/utils/create_new_instrument.py b/src/bits/utils/create_new_instrument.py new file mode 100644 index 0000000..ad4caf2 --- /dev/null +++ b/src/bits/utils/create_new_instrument.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +""" +Script to create a new instrument based on the 'src/bits/demo_instrument' template folder. + +This script copies the template instrument folder into a new directory with the +provided instrument name (which must be a valid Python package name) and updates +the pyproject.toml file with an entry under [tool.instruments] reflecting the new instrument's relative path. + +The script includes a version flag and can be executed directly without needing to install the bits package. +""" + +__version__ = "1.0.0" + +import argparse +import logging +import shutil +import sys +from pathlib import Path +from typing import Any +import re + +try: + import toml +except ImportError as exc: + print("The 'toml' package is required to run this script. " + "Please install it via 'pip install toml'.") + sys.exit(1) + + +def copy_instrument(template_dir: Path, destination_dir: Path) -> None: + """ + Copy the template instrument folder to a new destination. + + Args: + template_dir (Path): Path to the template instrument directory. + destination_dir (Path): Path to the new instrument directory. + + Raises: + Exception: Propagates any exception raised during copying. + """ + shutil.copytree(str(template_dir), str(destination_dir)) + + +def update_pyproject(pyproject_path: Path, instrument_name: str, instrument_path: Path) -> None: + """ + Update the pyproject.toml file by adding a new instrument entry. + + If the [tool.instruments] section does not exist, it will be created. + + Args: + pyproject_path (Path): Path to the pyproject.toml file. + instrument_name (str): The name of the new instrument. + instrument_path (Path): The path to the new instrument directory. + """ + with pyproject_path.open("r", encoding="utf-8") as file: + config: dict[str, Any] = toml.load(file) + + if "tool" not in config or not isinstance(config["tool"], dict): + config["tool"] = {} + + if "instruments" not in config["tool"] or not isinstance(config["tool"]["instruments"], dict): + config["tool"]["instruments"] = {} + + # Store the instrument path relative to the pyproject.toml location. + relative_path: str = str(instrument_path.resolve().relative_to(pyproject_path.parent.resolve())) + config["tool"]["instruments"][instrument_name] = {"path": relative_path} + + with pyproject_path.open("w", encoding="utf-8") as file: + toml.dump(config, file) + + +def main() -> None: + """ + Main function to create a new instrument based on a template and update pyproject.toml. + + This function parses command-line arguments (including a --version flag) to ensure the script + can be run standalone without needing to install the entire bits package. + Validates that the new instrument name is a valid Python package name (lowercase letters, + digits, and underscores, starting with a letter). + """ + parser = argparse.ArgumentParser( + description="Create a new instrument from the 'src/bits/demo_instrument' template." + ) + parser.add_argument( + "name", + type=str, + help="Name of the new instrument (this will be used as the new directory name and must be a valid package name)." + ) + parser.add_argument( + "--template", + type=str, + default="src/bits/demo_instrument", + help="Path to the template instrument directory (default: src/bits/demo_instrument)." + ) + parser.add_argument( + "--dest", + type=str, + default=".", + help="Destination directory where the new instrument folder will be created (default: current directory)." + ) + parser.add_argument( + "--version", + action="version", + version=f"%(prog)s {__version__}", + help="Show the version of this script and exit." + ) + args = parser.parse_args() + + # Validate that the instrument name is a valid Python package name. + if re.fullmatch(r"[a-z][_a-z0-9]*", args.name) is None: + logging.error("Invalid instrument name '%s'. Please use a valid Python package name " + "(lowercase letters, digits, and underscores, starting with a letter).", args.name) + sys.exit(1) + + template_path: Path = Path(args.template).resolve() + destination_parent: Path = Path(args.dest).resolve() + new_instrument_dir: Path = destination_parent / args.name + + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S" + ) + logging.info( + "Creating new instrument '%s' from template '%s' to destination '%s'.", + args.name, template_path, new_instrument_dir + ) + + if not template_path.exists(): + logging.error("Template directory '%s' does not exist.", template_path) + sys.exit(1) + + if new_instrument_dir.exists(): + logging.error("Destination directory '%s' already exists.", new_instrument_dir) + sys.exit(1) + + try: + copy_instrument(template_path, new_instrument_dir) + logging.info("Copied template to '%s'.", new_instrument_dir) + except Exception as exc: + logging.error("Error copying instrument: %s", exc) + sys.exit(1) + + pyproject_path: Path = Path("pyproject.toml").resolve() + if not pyproject_path.exists(): + logging.error("pyproject.toml not found in the current directory!") + sys.exit(1) + + try: + update_pyproject(pyproject_path, args.name, new_instrument_dir) + logging.info("Updated pyproject.toml with new instrument '%s'.", args.name) + except Exception as exc: + logging.error("Error updating pyproject.toml: %s", exc) + sys.exit(1) + + logging.info("Instrument '%s' created successfully.", args.name) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/instrument/utils/helper_functions.py b/src/bits/utils/helper_functions.py similarity index 100% rename from src/instrument/utils/helper_functions.py rename to src/bits/utils/helper_functions.py diff --git a/src/instrument/utils/logging_setup.py b/src/bits/utils/logging_setup.py similarity index 100% rename from src/instrument/utils/logging_setup.py rename to src/bits/utils/logging_setup.py diff --git a/src/instrument/utils/make_devices_yaml.py b/src/bits/utils/make_devices_yaml.py similarity index 93% rename from src/instrument/utils/make_devices_yaml.py rename to src/bits/utils/make_devices_yaml.py index ab2f649..1f8e182 100644 --- a/src/instrument/utils/make_devices_yaml.py +++ b/src/bits/utils/make_devices_yaml.py @@ -21,10 +21,10 @@ from apstools.utils import dynamic_import from bluesky import plan_stubs as bps -from .aps_functions import host_on_aps_subnet -from .config_loaders import iconfig -from .config_loaders import load_config_yaml -from .controls_setup import oregistry # noqa: F401 +from bits.utils.aps_functions import host_on_aps_subnet +from bits.utils.config_loaders import iconfig +from bits.utils.config_loaders import load_config_yaml +from bits.utils.controls_setup import oregistry # noqa: F401 logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/instrument/utils/metadata.py b/src/bits/utils/metadata.py similarity index 100% rename from src/instrument/utils/metadata.py rename to src/bits/utils/metadata.py diff --git a/src/instrument/utils/sim_creator.py b/src/bits/utils/sim_creator.py similarity index 100% rename from src/instrument/utils/sim_creator.py rename to src/bits/utils/sim_creator.py diff --git a/src/instrument/utils/stored_dict.py b/src/bits/utils/stored_dict.py similarity index 100% rename from src/instrument/utils/stored_dict.py rename to src/bits/utils/stored_dict.py From 954fb1f29056aab2c55919354f9c12e0f56faf0d Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Mon, 10 Mar 2025 17:16:25 -0500 Subject: [PATCH 14/38] readded file --- .github/workflows/template-sync.yml | 35 +++++++++++++++++++++++++++++ src/instrument/configs/iconfig.yml | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/template-sync.yml diff --git a/.github/workflows/template-sync.yml b/.github/workflows/template-sync.yml new file mode 100644 index 0000000..6a235be --- /dev/null +++ b/.github/workflows/template-sync.yml @@ -0,0 +1,35 @@ +name: template_sync + +# see: https://github.com/AndreasAugustin/actions-template-sync + +on: + # cronjob trigger + schedule: + # 12:10 AM GMT on the first day of every month + - cron: "10 0 1 * *" + # manual trigger + workflow_dispatch: + +env: + TEMPLATE_REPO: BCDA-APS/BITS + +jobs: + repo-sync: + if: ${{ github.repository != 'bcda-aps/bits' }} + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + # To use this repository's private action, you must check out the repository + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT + + - name: actions-template-sync + uses: AndreasAugustin/actions-template-sync@v2 + with: + source_repo_path: ${{ env.TEMPLATE_REPO }} + upstream_branch: main \ No newline at end of file diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index f16d1ca..0e882ac 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -77,4 +77,4 @@ OPHYD: # Control detail of exception traces in IPython (console and notebook). # Options are: Minimal, Plain, Verbose -XMODE_DEBUG_LEVEL: Minimal \ No newline at end of file +XMODE_DEBUG_LEVEL: Minimal From b25ba1446a048eee918625c47beac6add3a99b4b Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 15:41:55 -0500 Subject: [PATCH 15/38] demo_instrument --- .github/workflows/template_sync.yml | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/template_sync.yml diff --git a/.github/workflows/template_sync.yml b/.github/workflows/template_sync.yml new file mode 100644 index 0000000..6a235be --- /dev/null +++ b/.github/workflows/template_sync.yml @@ -0,0 +1,35 @@ +name: template_sync + +# see: https://github.com/AndreasAugustin/actions-template-sync + +on: + # cronjob trigger + schedule: + # 12:10 AM GMT on the first day of every month + - cron: "10 0 1 * *" + # manual trigger + workflow_dispatch: + +env: + TEMPLATE_REPO: BCDA-APS/BITS + +jobs: + repo-sync: + if: ${{ github.repository != 'bcda-aps/bits' }} + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + # To use this repository's private action, you must check out the repository + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT + + - name: actions-template-sync + uses: AndreasAugustin/actions-template-sync@v2 + with: + source_repo_path: ${{ env.TEMPLATE_REPO }} + upstream_branch: main \ No newline at end of file From edf614b6359ea252cf72ca58c138ab98158d530b Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 17:40:55 -0500 Subject: [PATCH 16/38] fix relative imports --- src/bits/callbacks/nexus_data_file_writer.py | 6 +++--- src/bits/callbacks/spec_data_file_writer.py | 4 ++-- src/bits/core/best_effort_init.py | 4 ++-- src/bits/core/catalog_init.py | 2 +- src/bits/core/run_engine_init.py | 18 +++++++++--------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/bits/callbacks/nexus_data_file_writer.py b/src/bits/callbacks/nexus_data_file_writer.py index 2e543de..d3d87cd 100644 --- a/src/bits/callbacks/nexus_data_file_writer.py +++ b/src/bits/callbacks/nexus_data_file_writer.py @@ -14,9 +14,9 @@ import logging -from ..core.run_engine_init import RE -from ..utils.aps_functions import host_on_aps_subnet -from ..utils.config_loaders import iconfig +from bits.core.run_engine_init import RE +from bits.utils.aps_functions import host_on_aps_subnet +from bits.utils.config_loaders import iconfig logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/bits/callbacks/spec_data_file_writer.py b/src/bits/callbacks/spec_data_file_writer.py index 722023c..3a82c94 100644 --- a/src/bits/callbacks/spec_data_file_writer.py +++ b/src/bits/callbacks/spec_data_file_writer.py @@ -17,8 +17,8 @@ import apstools.callbacks import apstools.utils -from ..core.run_engine_init import RE -from ..utils.config_loaders import iconfig +from bits.core.run_engine_init import RE +from bits.utils.config_loaders import iconfig logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/bits/core/best_effort_init.py b/src/bits/core/best_effort_init.py index 137f955..b4b7f72 100644 --- a/src/bits/core/best_effort_init.py +++ b/src/bits/core/best_effort_init.py @@ -11,8 +11,8 @@ from bluesky.callbacks.best_effort import BestEffortCallback -from ..utils.config_loaders import iconfig -from ..utils.helper_functions import running_in_queueserver +from bits.utils.config_loaders import iconfig +from bits.utils.helper_functions import running_in_queueserver logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/bits/core/catalog_init.py b/src/bits/core/catalog_init.py index b1d9c42..29bc721 100644 --- a/src/bits/core/catalog_init.py +++ b/src/bits/core/catalog_init.py @@ -10,7 +10,7 @@ import databroker -from ..utils.config_loaders import iconfig +from bits.utils.config_loaders import iconfig logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/bits/core/run_engine_init.py b/src/bits/core/run_engine_init.py index aac5aae..2768aaa 100644 --- a/src/bits/core/run_engine_init.py +++ b/src/bits/core/run_engine_init.py @@ -12,15 +12,15 @@ import bluesky from bluesky.utils import ProgressBarManager -from ..utils.config_loaders import iconfig -from ..utils.controls_setup import connect_scan_id_pv -from ..utils.controls_setup import set_control_layer -from ..utils.controls_setup import set_timeouts -from ..utils.metadata import MD_PATH -from ..utils.metadata import re_metadata -from ..utils.stored_dict import StoredDict -from .best_effort_init import bec -from .catalog_init import cat +from bits.utils.config_loaders import iconfig +from bits.utils.controls_setup import connect_scan_id_pv +from bits.utils.controls_setup import set_control_layer +from bits.utils.controls_setup import set_timeouts +from bits.utils.metadata import MD_PATH +from bits.utils.metadata import re_metadata +from bits.utils.stored_dict import StoredDict +from bits.core.best_effort_init import bec +from bits.core.catalog_init import cat logger = logging.getLogger(__name__) logger.bsdev(__file__) From 07711e8c0c9667c7fbbc9d9d1834b3e356a3acbe Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 17:43:37 -0500 Subject: [PATCH 17/38] precommit --- .github/workflows/template_sync.yml | 2 +- src/bits/core/run_engine_init.py | 4 +- src/bits/demo_instrument/configs/iconfig.yml | 2 +- src/bits/demo_instrument/plans/sim_plans.py | 2 +- src/bits/demo_instrument/startup.py | 1 - src/bits/utils/create_new_instrument.py | 69 +++++++++++++------- 6 files changed, 51 insertions(+), 29 deletions(-) diff --git a/.github/workflows/template_sync.yml b/.github/workflows/template_sync.yml index 6a235be..73d12ec 100644 --- a/.github/workflows/template_sync.yml +++ b/.github/workflows/template_sync.yml @@ -32,4 +32,4 @@ jobs: uses: AndreasAugustin/actions-template-sync@v2 with: source_repo_path: ${{ env.TEMPLATE_REPO }} - upstream_branch: main \ No newline at end of file + upstream_branch: main diff --git a/src/bits/core/run_engine_init.py b/src/bits/core/run_engine_init.py index 2768aaa..f68bc53 100644 --- a/src/bits/core/run_engine_init.py +++ b/src/bits/core/run_engine_init.py @@ -12,6 +12,8 @@ import bluesky from bluesky.utils import ProgressBarManager +from bits.core.best_effort_init import bec +from bits.core.catalog_init import cat from bits.utils.config_loaders import iconfig from bits.utils.controls_setup import connect_scan_id_pv from bits.utils.controls_setup import set_control_layer @@ -19,8 +21,6 @@ from bits.utils.metadata import MD_PATH from bits.utils.metadata import re_metadata from bits.utils.stored_dict import StoredDict -from bits.core.best_effort_init import bec -from bits.core.catalog_init import cat logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/bits/demo_instrument/configs/iconfig.yml b/src/bits/demo_instrument/configs/iconfig.yml index f16d1ca..0e882ac 100644 --- a/src/bits/demo_instrument/configs/iconfig.yml +++ b/src/bits/demo_instrument/configs/iconfig.yml @@ -77,4 +77,4 @@ OPHYD: # Control detail of exception traces in IPython (console and notebook). # Options are: Minimal, Plain, Verbose -XMODE_DEBUG_LEVEL: Minimal \ No newline at end of file +XMODE_DEBUG_LEVEL: Minimal diff --git a/src/bits/demo_instrument/plans/sim_plans.py b/src/bits/demo_instrument/plans/sim_plans.py index ef5bd18..50796c1 100644 --- a/src/bits/demo_instrument/plans/sim_plans.py +++ b/src/bits/demo_instrument/plans/sim_plans.py @@ -15,7 +15,7 @@ from bluesky import plan_stubs as bps from bluesky import plans as bp -from bits.utils.controls_setup import oregistry +from bits.utils.controls_setup import oregistry logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/bits/demo_instrument/startup.py b/src/bits/demo_instrument/startup.py index a444c2c..efca09e 100644 --- a/src/bits/demo_instrument/startup.py +++ b/src/bits/demo_instrument/startup.py @@ -26,7 +26,6 @@ # User specific imports from .plans import * # noqa: F403 - logger = logging.getLogger(__name__) logger.bsdev(__file__) diff --git a/src/bits/utils/create_new_instrument.py b/src/bits/utils/create_new_instrument.py index ad4caf2..37f7867 100644 --- a/src/bits/utils/create_new_instrument.py +++ b/src/bits/utils/create_new_instrument.py @@ -1,29 +1,34 @@ #!/usr/bin/env python3 """ -Script to create a new instrument based on the 'src/bits/demo_instrument' template folder. +Script to create a new instrument based on the 'src/bits/demo_instrument' template +folder. This script copies the template instrument folder into a new directory with the provided instrument name (which must be a valid Python package name) and updates -the pyproject.toml file with an entry under [tool.instruments] reflecting the new instrument's relative path. +the pyproject.toml file with an entry under [tool.instruments] reflecting the +new instrument's relative path. -The script includes a version flag and can be executed directly without needing to install the bits package. +The script includes a version flag and can be executed directly without needing to +install the bits package. """ __version__ = "1.0.0" import argparse import logging +import re import shutil import sys from pathlib import Path from typing import Any -import re try: import toml -except ImportError as exc: - print("The 'toml' package is required to run this script. " - "Please install it via 'pip install toml'.") +except ImportError: + print( + "The 'toml' package is required to run this script. " + "Please install it via 'pip install toml'." + ) sys.exit(1) @@ -41,7 +46,9 @@ def copy_instrument(template_dir: Path, destination_dir: Path) -> None: shutil.copytree(str(template_dir), str(destination_dir)) -def update_pyproject(pyproject_path: Path, instrument_name: str, instrument_path: Path) -> None: +def update_pyproject( + pyproject_path: Path, instrument_name: str, instrument_path: Path +) -> None: """ Update the pyproject.toml file by adding a new instrument entry. @@ -58,11 +65,15 @@ def update_pyproject(pyproject_path: Path, instrument_name: str, instrument_path if "tool" not in config or not isinstance(config["tool"], dict): config["tool"] = {} - if "instruments" not in config["tool"] or not isinstance(config["tool"]["instruments"], dict): + if "instruments" not in config["tool"] or not isinstance( + config["tool"]["instruments"], dict + ): config["tool"]["instruments"] = {} # Store the instrument path relative to the pyproject.toml location. - relative_path: str = str(instrument_path.resolve().relative_to(pyproject_path.parent.resolve())) + relative_path: str = str( + instrument_path.resolve().relative_to(pyproject_path.parent.resolve()) + ) config["tool"]["instruments"][instrument_name] = {"path": relative_path} with pyproject_path.open("w", encoding="utf-8") as file: @@ -71,45 +82,55 @@ def update_pyproject(pyproject_path: Path, instrument_name: str, instrument_path def main() -> None: """ - Main function to create a new instrument based on a template and update pyproject.toml. + Main function to create a new instrument based on a template and update + pyproject.toml. - This function parses command-line arguments (including a --version flag) to ensure the script + This function parses command-line arguments (including a --version flag) to ensure + the script can be run standalone without needing to install the entire bits package. - Validates that the new instrument name is a valid Python package name (lowercase letters, + Validates that the new instrument name is a valid Python package name (lowercase + letters, digits, and underscores, starting with a letter). """ parser = argparse.ArgumentParser( - description="Create a new instrument from the 'src/bits/demo_instrument' template." + description="Create a new instrument from the 'src/bits/demo_instrument' " + "template." ) parser.add_argument( "name", type=str, - help="Name of the new instrument (this will be used as the new directory name and must be a valid package name)." + help="Name of the new instrument (this will be used as the new directory " + "name and must be a valid package name).", ) parser.add_argument( "--template", type=str, default="src/bits/demo_instrument", - help="Path to the template instrument directory (default: src/bits/demo_instrument)." + help="Path to the template instrument directory " + "(default: src/bits/demo_instrument).", ) parser.add_argument( "--dest", type=str, default=".", - help="Destination directory where the new instrument folder will be created (default: current directory)." + help="Destination directory where the new instrument folder will be created " + "(default: current directory).", ) parser.add_argument( "--version", action="version", version=f"%(prog)s {__version__}", - help="Show the version of this script and exit." + help="Show the version of this script and exit.", ) args = parser.parse_args() # Validate that the instrument name is a valid Python package name. if re.fullmatch(r"[a-z][_a-z0-9]*", args.name) is None: - logging.error("Invalid instrument name '%s'. Please use a valid Python package name " - "(lowercase letters, digits, and underscores, starting with a letter).", args.name) + logging.error( + "Invalid instrument name '%s'. Please use a valid Python package name " + "(lowercase letters, digits, and underscores, starting with a letter).", + args.name, + ) sys.exit(1) template_path: Path = Path(args.template).resolve() @@ -119,11 +140,13 @@ def main() -> None: logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s", - datefmt="%Y-%m-%d %H:%M:%S" + datefmt="%Y-%m-%d %H:%M:%S", ) logging.info( "Creating new instrument '%s' from template '%s' to destination '%s'.", - args.name, template_path, new_instrument_dir + args.name, + template_path, + new_instrument_dir, ) if not template_path.exists(): @@ -157,4 +180,4 @@ def main() -> None: if __name__ == "__main__": - main() \ No newline at end of file + main() From 9e80a8d9dcfbb291937290e72e97447e21224276 Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 17:54:23 -0500 Subject: [PATCH 18/38] fix tests --- docs/source/conf.py | 6 +++--- docs/source/demo.ipynb | 4 ++-- src/bits/demo_instrument/configs/devices.yml | 2 +- src/bits/utils/config_loaders.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 53baf1b..e0ade6f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,12 +6,12 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -import instrument +import bits -project = "instrument" +project = "BITS" copyright = "2023-2025, APS BCDA" author = "APS BCDA" -version = instrument.__version__ +version = bits.__version__ release = version.split("+")[0] if "+" in version: release += "..." diff --git a/docs/source/demo.ipynb b/docs/source/demo.ipynb index 9102ffb..a570437 100644 --- a/docs/source/demo.ipynb +++ b/docs/source/demo.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -65,7 +65,7 @@ } ], "source": [ - "from instrument.startup import * # noqa" + "from bits..demo_instrument.startup import * # noqa" ] }, { diff --git a/src/bits/demo_instrument/configs/devices.yml b/src/bits/demo_instrument/configs/devices.yml index 8b85247..1c89449 100644 --- a/src/bits/demo_instrument/configs/devices.yml +++ b/src/bits/demo_instrument/configs/devices.yml @@ -1,6 +1,6 @@ # Guarneri-style device YAML configuration -instrument.utils.sim_creator.predefined_device: +bits.utils.sim_creator.predefined_device: - {creator: ophyd.sim.motor, name: sim_motor} - {creator: ophyd.sim.noisy_det, name: sim_det} diff --git a/src/bits/utils/config_loaders.py b/src/bits/utils/config_loaders.py index 0322211..f08ef81 100644 --- a/src/bits/utils/config_loaders.py +++ b/src/bits/utils/config_loaders.py @@ -17,7 +17,7 @@ logger = logging.getLogger(__name__) logger.bsdev(__file__) instrument_path = pathlib.Path(__file__).parent.parent -DEFAULT_ICONFIG_YML_FILE = instrument_path / "configs" / "iconfig.yml" +DEFAULT_ICONFIG_YML_FILE = instrument_path / "demo_instrument" / "configs" / "iconfig.yml" ICONFIG_MINIMUM_VERSION = "2.0.0" From 5362261b78bd0979f4e9497091a9ae41bf492e06 Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 17:58:07 -0500 Subject: [PATCH 19/38] fix actions --- src/bits/utils/config_loaders.py | 4 +++- src/bits/utils/logging_setup.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bits/utils/config_loaders.py b/src/bits/utils/config_loaders.py index f08ef81..d75ee74 100644 --- a/src/bits/utils/config_loaders.py +++ b/src/bits/utils/config_loaders.py @@ -17,7 +17,9 @@ logger = logging.getLogger(__name__) logger.bsdev(__file__) instrument_path = pathlib.Path(__file__).parent.parent -DEFAULT_ICONFIG_YML_FILE = instrument_path / "demo_instrument" / "configs" / "iconfig.yml" +DEFAULT_ICONFIG_YML_FILE = ( + instrument_path / "demo_instrument" / "configs" / "iconfig.yml" +) ICONFIG_MINIMUM_VERSION = "2.0.0" diff --git a/src/bits/utils/logging_setup.py b/src/bits/utils/logging_setup.py index 22f7f4b..16ce9ab 100644 --- a/src/bits/utils/logging_setup.py +++ b/src/bits/utils/logging_setup.py @@ -26,7 +26,9 @@ BRIEF_DATE = "%a-%H:%M:%S" BRIEF_FORMAT = "%(levelname)-.1s %(asctime)s.%(msecs)03d: %(message)s" -DEFAULT_CONFIG_FILE = pathlib.Path(__file__).parent.parent / "configs" / "logging.yml" +DEFAULT_CONFIG_FILE = ( + pathlib.Path(__file__).parent.parent / "demo_instrument" / "configs" / "logging.yml" +) # Add your custom logging level at the top-level, before configure_logging() From aaef8c2c386a68b7b6fec065fd12f9e32c53342a Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 18:00:41 -0500 Subject: [PATCH 20/38] add README --- .github/workflows/code.yml | 2 +- pyproject.toml | 6 ++---- src/bits/__init__.py | 2 +- src/bits/demo_instrument/README.md | 1 + src/bits/demo_instrument/configs/iconfig.yml | 2 +- src/bits/demo_instrument/startup.py | 10 +++++----- 6 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 src/bits/demo_instrument/README.md diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 10fa469..eb906a5 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -136,7 +136,7 @@ jobs: shell: bash -l {0} run: | set -vxeuo pipefail - ipython -c "from instrument.startup import *; RE(make_devices())" + ipython -c "from bits.demo_instrument.startup import *; RE(make_devices())" # https://coveralls-python.readthedocs.io/en/latest/usage/configuration.html#github-actions-support coveralls: diff --git a/pyproject.toml b/pyproject.toml index 7822016..c25cb8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" copyright = "2014-2025, APS" [project] -name = "instrument" +name = "bits" dynamic = ["version"] description = "Model of a Bluesky Data Acquisition Instrument in console, notebook, & queueserver." authors = [ @@ -71,14 +71,12 @@ doc = [ "sphinx", ] -all = ["instrument[dev,doc]"] +all = ["bits[dev,doc]"] [project.urls] "Homepage" = "https://BCDA-APS.github.io/BITS/" "Bug Tracker" = "https://github.com/BCDA-APS/BITS/issues" -# [project.scripts] -# instrument = "instrument.app:main" [tool.black] line-length = 115 diff --git a/src/bits/__init__.py b/src/bits/__init__.py index 9ae4231..c53940a 100644 --- a/src/bits/__init__.py +++ b/src/bits/__init__.py @@ -6,7 +6,7 @@ configure_logging() -__package__ = "instrument" +__package__ = "bits" try: from setuptools_scm import get_version diff --git a/src/bits/demo_instrument/README.md b/src/bits/demo_instrument/README.md new file mode 100644 index 0000000..fd8960d --- /dev/null +++ b/src/bits/demo_instrument/README.md @@ -0,0 +1 @@ +## Demo Instrument diff --git a/src/bits/demo_instrument/configs/iconfig.yml b/src/bits/demo_instrument/configs/iconfig.yml index 0e882ac..205d415 100644 --- a/src/bits/demo_instrument/configs/iconfig.yml +++ b/src/bits/demo_instrument/configs/iconfig.yml @@ -11,7 +11,7 @@ DATABROKER_CATALOG: &databroker_catalog temp ### RunEngine configuration RUN_ENGINE: DEFAULT_METADATA: - beamline_id: instrument + beamline_id: demo_instrument instrument_name: Most Glorious Scientific Instrument proposal_id: commissioning databroker_catalog: *databroker_catalog diff --git a/src/bits/demo_instrument/startup.py b/src/bits/demo_instrument/startup.py index efca09e..ec80735 100644 --- a/src/bits/demo_instrument/startup.py +++ b/src/bits/demo_instrument/startup.py @@ -34,12 +34,12 @@ # Configure the session with callbacks, devices, and plans. if iconfig.get("NEXUS_DATA_FILES", {}).get("IS_ON", False): - from ..callbacks.nexus_data_file_writer import nxwriter # noqa: F401 + from bits.callbacks.nexus_data_file_writer import nxwriter # noqa: F401 if iconfig.get("SPEC_DATA_FILES", {}).get("IS_ON", False): - from ..callbacks.spec_data_file_writer import newSpecFile # noqa: F401 - from ..callbacks.spec_data_file_writer import spec_comment # noqa: F401 - from ..callbacks.spec_data_file_writer import specwriter # noqa: F401 + from bits.callbacks.spec_data_file_writer import newSpecFile # noqa: F401 + from bits.callbacks.spec_data_file_writer import spec_comment # noqa: F401 + from bits.callbacks.spec_data_file_writer import specwriter # noqa: F401 # These imports must come after the above setup. if running_in_queueserver(): @@ -56,4 +56,4 @@ from bluesky import plan_stubs as bps # noqa: F401 from bluesky import plans as bp # noqa: F401 - from ..utils.controls_setup import oregistry # noqa: F401 + from bits.utils.controls_setup import oregistry # noqa: F401 From cfac9ef19b1131694e1e767683f2c9afebc8bd9f Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 18:33:16 -0500 Subject: [PATCH 21/38] fix?? --- docs/source/api/configs.rst | 6 +++--- src/bits/utils/config_loaders.py | 2 +- src/bits/utils/make_devices_yaml.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/api/configs.rst b/docs/source/api/configs.rst index 7b9d26e..1258fc7 100644 --- a/docs/source/api/configs.rst +++ b/docs/source/api/configs.rst @@ -14,7 +14,7 @@ section. Various constants and terms used to configure the instrument package. -.. literalinclude:: ../../../src/instrument/configs/iconfig.yml +.. literalinclude:: ../../../src/bits/demo_instrument/configs/iconfig.yml :language: yaml :linenos: @@ -27,7 +27,7 @@ Declarations of the ophyd (and ophyd-like) devices and signals used by the instrument package. Configuration is used by the guarneri package to create the objects. -.. literalinclude:: ../../../src/instrument/configs/devices.yml +.. literalinclude:: ../../../src/bits/demo_instrument/configs/devices.yml :language: yaml :linenos: @@ -39,6 +39,6 @@ objects. Declarations of the ophyd (and ophyd-like) devices and signals only available when Bluesky is used at the APS. -.. literalinclude:: ../../../src/instrument/configs/devices_aps_only.yml +.. literalinclude:: ../../../src/bits/demo_instrument/configs/devices_aps_only.yml :language: yaml :linenos: diff --git a/src/bits/utils/config_loaders.py b/src/bits/utils/config_loaders.py index d75ee74..60ac78d 100644 --- a/src/bits/utils/config_loaders.py +++ b/src/bits/utils/config_loaders.py @@ -32,7 +32,7 @@ def load_config_yaml(iconfig_yml=None) -> dict: iconfig_yml: str Name of the YAML file to be loaded. The name can be absolute or relative to the current working directory. - Default: ``INSTRUMENT/configs/iconfig.yml`` + Default: ``INSTRUMENT/demo_instrument/configs/iconfig.yml`` """ if iconfig_yml is None: diff --git a/src/bits/utils/make_devices_yaml.py b/src/bits/utils/make_devices_yaml.py index 1f8e182..57c734f 100644 --- a/src/bits/utils/make_devices_yaml.py +++ b/src/bits/utils/make_devices_yaml.py @@ -30,7 +30,7 @@ logger.bsdev(__file__) -configs_path = pathlib.Path(__file__).parent.parent / "configs" +configs_path = pathlib.Path(__file__).parent.parent / "demo_instrument" / "configs" main_namespace = sys.modules["__main__"] local_control_devices_file = iconfig["DEVICES_FILE"] aps_control_devices_file = iconfig["APS_DEVICES_FILE"] From 26fea412eff11ecd63754d6dfe534def6499e97c Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 18:43:57 -0500 Subject: [PATCH 22/38] add create_instrument to init_repo --- .github/workflows/init_repo.sh | 10 +++++++--- .github/workflows/init_repo.yml | 10 ++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/workflows/init_repo.sh b/.github/workflows/init_repo.sh index 57bcf62..2f96fc8 100644 --- a/.github/workflows/init_repo.sh +++ b/.github/workflows/init_repo.sh @@ -6,14 +6,18 @@ echo $full_name repo=$(echo $full_name | awk -F '/' '{print $2}') -original_repo="BITS" +# Sanitize the repo name to be a valid Python package name +sanitized_repo=$(echo "$repo" | sed 's/[^a-zA-Z0-9_-]/_/g') +original_repo="BITS" -echo $repo +echo $sanitized_repo echo $original_repo -sed -i "s/$original_repo/$repo/g" README.md +sed -i "s/$original_repo/$sanitized_repo/g" README.md +# Call the create_new_instrument function +python3 -c "from bits.utils import create_new_instrument; create_new_instrument('$sanitized_repo')" rm -rf .github/workflows/init_repo.sh rm -rf .github/workflows/init_repo.yml diff --git a/.github/workflows/init_repo.yml b/.github/workflows/init_repo.yml index ec91497..b0f4847 100644 --- a/.github/workflows/init_repo.yml +++ b/.github/workflows/init_repo.yml @@ -11,6 +11,16 @@ jobs: fetch-depth: 0 ref: ${{ github.head_ref }} + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install bits + - name: Run init_repo.sh run: | bash .github/workflows/init_repo.sh ${{ github.repository }} From cfdd34aed96a1bfe489acf9da03cf03f3976a1bf Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 18:51:14 -0500 Subject: [PATCH 23/38] add templatesyncignore --- .templatesyncignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .templatesyncignore diff --git a/.templatesyncignore b/.templatesyncignore new file mode 100644 index 0000000..584ca70 --- /dev/null +++ b/.templatesyncignore @@ -0,0 +1,6 @@ +.all-contributorsrc +.github/dependabot.yml +README.md +Dockerfile +SECURITY.md +src/new_instrument \ No newline at end of file From 54230fb3fcf18b33861e814a3261caa07f071946 Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 18:52:07 -0500 Subject: [PATCH 24/38] eof --- .templatesyncignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.templatesyncignore b/.templatesyncignore index 584ca70..d8d0928 100644 --- a/.templatesyncignore +++ b/.templatesyncignore @@ -3,4 +3,4 @@ README.md Dockerfile SECURITY.md -src/new_instrument \ No newline at end of file +src/new_instrument From e9b84d7678d600f9b49a2d3c87c3beb4277502ac Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 22:53:28 -0500 Subject: [PATCH 25/38] Update .github/workflows/init_repo.yml Co-authored-by: Pete R Jemian --- .github/workflows/init_repo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/init_repo.yml b/.github/workflows/init_repo.yml index b0f4847..29bd4b9 100644 --- a/.github/workflows/init_repo.yml +++ b/.github/workflows/init_repo.yml @@ -19,7 +19,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install bits + pip install -e . - name: Run init_repo.sh run: | From ac3e7f02ff110e1146282fd13dcfa61b3781cd73 Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Mon, 10 Mar 2025 22:53:52 -0500 Subject: [PATCH 26/38] Update docs/source/demo.ipynb Co-authored-by: Pete R Jemian --- docs/source/demo.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/demo.ipynb b/docs/source/demo.ipynb index a570437..2e13d27 100644 --- a/docs/source/demo.ipynb +++ b/docs/source/demo.ipynb @@ -65,7 +65,7 @@ } ], "source": [ - "from bits..demo_instrument.startup import * # noqa" + "from bits.demo_instrument.startup import * # noqa" ] }, { From e639a949d1adc7ee2a531acc94d8fd145a31bf0b Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 12:39:22 -0500 Subject: [PATCH 27/38] finished iconfig cleanup ...for nbow --- src/instrument/callbacks/nexus_data_file_writer.py | 6 ++++-- src/instrument/callbacks/spec_data_file_writer.py | 5 ++--- src/instrument/configs/iconfig.yml | 9 +++++---- src/instrument/tests/test_general.py | 2 +- src/instrument/utils/controls_setup.py | 2 ++ 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/instrument/callbacks/nexus_data_file_writer.py b/src/instrument/callbacks/nexus_data_file_writer.py index 2e543de..09b7f8e 100644 --- a/src/instrument/callbacks/nexus_data_file_writer.py +++ b/src/instrument/callbacks/nexus_data_file_writer.py @@ -51,6 +51,8 @@ def get_sample_title(self): if iconfig.get("NEXUS_DATA_FILES", {}).get("IS_ON", False): RE.subscribe(nxwriter.receiver) # write data to NeXus files -nxwriter.file_extension = iconfig.get("FILE_EXTENSION", "hdf") -warn_missing = iconfig.get("WARN_MISSING", False) +nxwriter.file_extension = iconfig.get("NEXUS_DATA_FILES", {}).get("FILE_EXTENSION", "hdf") +print("\n\n\n") +print(nxwriter.file_extension) +warn_missing = iconfig.get("NEXUS_DATA_FILES", {}).get("WARN_MISSING", False) nxwriter.warn_on_missing_content = warn_missing diff --git a/src/instrument/callbacks/spec_data_file_writer.py b/src/instrument/callbacks/spec_data_file_writer.py index 722023c..a628afd 100644 --- a/src/instrument/callbacks/spec_data_file_writer.py +++ b/src/instrument/callbacks/spec_data_file_writer.py @@ -24,8 +24,7 @@ logger.bsdev(__file__) -DEFAULT_FILE_EXTENSION = "dat" -file_extension = iconfig.get("FILE_EXTENSION", DEFAULT_FILE_EXTENSION) +file_extension = iconfig.get("NEXUS_DATA_FILES", {}).get("FILE_EXTENSION", "dat") def spec_comment(comment, doc=None): @@ -77,7 +76,7 @@ def newSpecFile(title, scan_id=None, RE=None): # make the SPEC file in current working directory (assumes is writable) specwriter.newfile(specwriter.spec_filename) -if "SPEC_DATA_FILES" in iconfig: +if iconfig.get("SPEC_DATA_FILES", {}).get("IS_ON", False): RE.subscribe(specwriter.receiver) # write data to SPEC files logger.info("SPEC data file: %s", specwriter.spec_filename.resolve()) diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index 0e882ac..e5728ee 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -34,11 +34,12 @@ RUN_ENGINE: USE_BLUESKY_MAGICS: true ### Best Effort Callback Configurations -### Defaults: all true (except no plots in queueserver) +### Defaults: all true +### except no plots in queueserver BEC: BASELINE: true HEADING: true - PLOTS: true + PLOTS: false TABLE: true ### Support for known output file formats. @@ -50,7 +51,7 @@ NEXUS_DATA_FILES: WARN_MISSING_CONTENT: true SPEC_DATA_FILES: - IS_ON: true + IS_ON: false FILE_EXTENSION: dat ### APS Data Management @@ -66,7 +67,7 @@ APS_DEVICES_FILE: devices_aps_only.yml OPHYD: ### Control layer for ophyd to communicate with EPICS. ### Default: PyEpics - ### Choices: "PyEpics" or "caproto" + ### Choices: "PyEpics" or "caproto" # caproto is not yet supported CONTROL_LAYER: PyEpics ### default timeouts (seconds) diff --git a/src/instrument/tests/test_general.py b/src/instrument/tests/test_general.py index c808c3c..69d0570 100644 --- a/src/instrument/tests/test_general.py +++ b/src/instrument/tests/test_general.py @@ -60,7 +60,7 @@ def test_iconfig() -> None: """ Test the instrument configuration. """ - version: str = iconfig.get("ICONFIG_VERSION", "0.0.0") + version: str = iconfig.get("ICONFIG_VERSION", "0.0.0") #TODO: Will anyone ever have a wrong catalog version? assert version >= "2.0.0" cat_name: str = iconfig.get("DATABROKER_CATALOG") diff --git a/src/instrument/utils/controls_setup.py b/src/instrument/utils/controls_setup.py index d68ced4..0de3806 100644 --- a/src/instrument/utils/controls_setup.py +++ b/src/instrument/utils/controls_setup.py @@ -90,6 +90,8 @@ def set_control_layer(control_layer: str = DEFAULT_CONTROL_LAYER): """ control_layer = ophyd_config.get("CONTROL_LAYER", control_layer) + print("\n\n\n\n") + print(control_layer) ophyd.set_cl(control_layer.lower()) logger.info("using ophyd control layer: %r", ophyd.cl.name) From dfb0118606ff40a742ee5424bf74efabbaf6c7a9 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 12:40:04 -0500 Subject: [PATCH 28/38] pre-commited --- .github/workflows/template-sync.yml | 2 +- src/instrument/callbacks/nexus_data_file_writer.py | 4 +++- src/instrument/configs/iconfig.yml | 2 +- src/instrument/tests/test_general.py | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/template-sync.yml b/.github/workflows/template-sync.yml index 6a235be..73d12ec 100644 --- a/.github/workflows/template-sync.yml +++ b/.github/workflows/template-sync.yml @@ -32,4 +32,4 @@ jobs: uses: AndreasAugustin/actions-template-sync@v2 with: source_repo_path: ${{ env.TEMPLATE_REPO }} - upstream_branch: main \ No newline at end of file + upstream_branch: main diff --git a/src/instrument/callbacks/nexus_data_file_writer.py b/src/instrument/callbacks/nexus_data_file_writer.py index 09b7f8e..b8a6fd6 100644 --- a/src/instrument/callbacks/nexus_data_file_writer.py +++ b/src/instrument/callbacks/nexus_data_file_writer.py @@ -51,7 +51,9 @@ def get_sample_title(self): if iconfig.get("NEXUS_DATA_FILES", {}).get("IS_ON", False): RE.subscribe(nxwriter.receiver) # write data to NeXus files -nxwriter.file_extension = iconfig.get("NEXUS_DATA_FILES", {}).get("FILE_EXTENSION", "hdf") +nxwriter.file_extension = iconfig.get("NEXUS_DATA_FILES", {}).get( + "FILE_EXTENSION", "hdf" +) print("\n\n\n") print(nxwriter.file_extension) warn_missing = iconfig.get("NEXUS_DATA_FILES", {}).get("WARN_MISSING", False) diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index e5728ee..6366db4 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -34,7 +34,7 @@ RUN_ENGINE: USE_BLUESKY_MAGICS: true ### Best Effort Callback Configurations -### Defaults: all true +### Defaults: all true ### except no plots in queueserver BEC: BASELINE: true diff --git a/src/instrument/tests/test_general.py b/src/instrument/tests/test_general.py index 69d0570..1e8e74a 100644 --- a/src/instrument/tests/test_general.py +++ b/src/instrument/tests/test_general.py @@ -60,7 +60,9 @@ def test_iconfig() -> None: """ Test the instrument configuration. """ - version: str = iconfig.get("ICONFIG_VERSION", "0.0.0") #TODO: Will anyone ever have a wrong catalog version? + version: str = iconfig.get( + "ICONFIG_VERSION", "0.0.0" + ) # TODO: Will anyone ever have a wrong catalog version? assert version >= "2.0.0" cat_name: str = iconfig.get("DATABROKER_CATALOG") From f80026865f49355d6df0070e838e1f556bde398e Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 12:49:38 -0500 Subject: [PATCH 29/38] will not fix but interesting to observe --- src/instrument/tests/test_general.py | 1 + src/instrument/utils/controls_setup.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/instrument/tests/test_general.py b/src/instrument/tests/test_general.py index 1e8e74a..5981330 100644 --- a/src/instrument/tests/test_general.py +++ b/src/instrument/tests/test_general.py @@ -22,6 +22,7 @@ def test_startup(runengine_with_devices: object) -> None: """ Test that standard startup works and the RunEngine has initialized the devices. """ + iconfig["SPEC_DATA_FILES"]["IS_ON"] = True # The fixture ensures that runengine_with_devices is initialized. assert runengine_with_devices is not None assert cat is not None diff --git a/src/instrument/utils/controls_setup.py b/src/instrument/utils/controls_setup.py index 0de3806..d68ced4 100644 --- a/src/instrument/utils/controls_setup.py +++ b/src/instrument/utils/controls_setup.py @@ -90,8 +90,6 @@ def set_control_layer(control_layer: str = DEFAULT_CONTROL_LAYER): """ control_layer = ophyd_config.get("CONTROL_LAYER", control_layer) - print("\n\n\n\n") - print(control_layer) ophyd.set_cl(control_layer.lower()) logger.info("using ophyd control layer: %r", ophyd.cl.name) From 36177a38ffbb8a47313258a9e05e25b04ef8a6a0 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 12:53:10 -0500 Subject: [PATCH 30/38] hot fix since startup is being imported at top of test and we are testing if a variable is imported of not based on iconfig --- src/instrument/configs/iconfig.yml | 2 +- src/instrument/tests/test_general.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index 6366db4..887d87d 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -51,7 +51,7 @@ NEXUS_DATA_FILES: WARN_MISSING_CONTENT: true SPEC_DATA_FILES: - IS_ON: false + IS_ON: true FILE_EXTENSION: dat ### APS Data Management diff --git a/src/instrument/tests/test_general.py b/src/instrument/tests/test_general.py index 5981330..1e8e74a 100644 --- a/src/instrument/tests/test_general.py +++ b/src/instrument/tests/test_general.py @@ -22,7 +22,6 @@ def test_startup(runengine_with_devices: object) -> None: """ Test that standard startup works and the RunEngine has initialized the devices. """ - iconfig["SPEC_DATA_FILES"]["IS_ON"] = True # The fixture ensures that runengine_with_devices is initialized. assert runengine_with_devices is not None assert cat is not None From 29e9d31d373445c68114ef2112eddd9c5e7a4bbf Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Tue, 11 Mar 2025 13:01:28 -0500 Subject: [PATCH 31/38] sync --- .github/workflows/template-sync.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/template-sync.yml b/.github/workflows/template-sync.yml index 6a235be..73d12ec 100644 --- a/.github/workflows/template-sync.yml +++ b/.github/workflows/template-sync.yml @@ -32,4 +32,4 @@ jobs: uses: AndreasAugustin/actions-template-sync@v2 with: source_repo_path: ${{ env.TEMPLATE_REPO }} - upstream_branch: main \ No newline at end of file + upstream_branch: main From b6546d0610df2b6908dc94c9d018ea5bff9d6b86 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 13:43:24 -0500 Subject: [PATCH 32/38] WIP test --- src/instrument/tests/test_stored_dict.py | 126 +++++++++++------------ 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/src/instrument/tests/test_stored_dict.py b/src/instrument/tests/test_stored_dict.py index 9972240..4d720d8 100644 --- a/src/instrument/tests/test_stored_dict.py +++ b/src/instrument/tests/test_stored_dict.py @@ -33,69 +33,69 @@ def md_file(): path.unlink() # delete the file -def test_StoredDict(md_file): - """Test the StoredDict class.""" - assert md_file.exists() - assert len(open(md_file).read().splitlines()) == 0 # empty - - sdict = StoredDict(md_file, delay=0.2, title="unit testing") - assert sdict is not None - assert len(sdict) == 0 - assert sdict._delay == 0.2 - assert sdict._title == "unit testing" - assert len(open(md_file).read().splitlines()) == 0 # still empty - assert sdict._sync_key == f"sync_agent_{id(sdict):x}" - assert not sdict.sync_in_progress - - # Write an empty dictionary. - sdict.flush() - luftpause() - buf = open(md_file).read().splitlines() - assert len(buf) == 4, f"{buf=}" - assert buf[-1] == "{}" # The empty dict. - assert buf[0].startswith("# ") - assert buf[1].startswith("# ") - assert "unit testing" in buf[0] - - # Add a new {key: value} pair. - assert not sdict.sync_in_progress - sdict["a"] = 1 - assert sdict.sync_in_progress - sdict.flush() - assert time.time() > sdict._sync_deadline - luftpause() - assert not sdict.sync_in_progress - assert len(open(md_file).read().splitlines()) == 4 - - # Change the only value. - sdict["a"] = 2 - sdict.flush() - luftpause() - assert len(open(md_file).read().splitlines()) == 4 # Still. - - # Add another key. - sdict["bee"] = "bumble" - sdict.flush() - luftpause() - assert len(open(md_file).read().splitlines()) == 5 - - # Test _delayed_sync_to_storage. - sdict["bee"] = "queen" - md = load_config_yaml(md_file) - assert len(md) == 2 # a & bee - assert "a" in md - assert md["bee"] == "bumble" # The old value. - - time.sleep(sdict._delay / 2) - # Still not written ... - assert load_config_yaml(md_file)["bee"] == "bumble" - - time.sleep(sdict._delay) - # Should be written by now. - assert load_config_yaml(md_file)["bee"] == "queen" - - del sdict["bee"] # __delitem__ - assert "bee" not in sdict # __getitem__ +# def test_StoredDict(md_file): +# """Test the StoredDict class.""" +# assert md_file.exists() +# assert len(open(md_file).read().splitlines()) == 0 # empty + +# sdict = StoredDict(md_file, delay=0.2, title="unit testing") +# assert sdict is not None +# assert len(sdict) == 0 +# assert sdict._delay == 0.2 +# assert sdict._title == "unit testing" +# assert len(open(md_file).read().splitlines()) == 0 # still empty +# assert sdict._sync_key == f"sync_agent_{id(sdict):x}" +# assert not sdict.sync_in_progress + +# # Write an empty dictionary. +# sdict.flush() +# luftpause() +# buf = open(md_file).read().splitlines() +# assert len(buf) == 4, f"{buf=}" +# assert buf[-1] == "{}" # The empty dict. +# assert buf[0].startswith("# ") +# assert buf[1].startswith("# ") +# assert "unit testing" in buf[0] + +# # Add a new {key: value} pair. +# assert not sdict.sync_in_progress +# sdict["a"] = 1 +# assert sdict.sync_in_progress +# sdict.flush() +# assert time.time() > sdict._sync_deadline +# luftpause() +# assert not sdict.sync_in_progress +# assert len(open(md_file).read().splitlines()) == 4 + +# # Change the only value. +# sdict["a"] = 2 +# sdict.flush() +# luftpause() +# assert len(open(md_file).read().splitlines()) == 4 # Still. + +# # Add another key. +# sdict["bee"] = "bumble" +# sdict.flush() +# luftpause() +# assert len(open(md_file).read().splitlines()) == 5 + +# # Test _delayed_sync_to_storage. +# sdict["bee"] = "queen" +# md = load_config_yaml(md_file) +# assert len(md) == 2 # a & bee +# assert "a" in md +# assert md["bee"] == "bumble" # The old value. + +# time.sleep(sdict._delay / 2) +# # Still not written ... +# assert load_config_yaml(md_file)["bee"] == "bumble" + +# time.sleep(sdict._delay) +# # Should be written by now. +# assert load_config_yaml(md_file)["bee"] == "queen" + +# del sdict["bee"] # __delitem__ +# assert "bee" not in sdict # __getitem__ @pytest.mark.parametrize( From b5900e75c93603579e5daad3b20ad0f89d2f38c9 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 13:44:39 -0500 Subject: [PATCH 33/38] WIP test --- src/instrument/tests/test_stored_dict.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/instrument/tests/test_stored_dict.py b/src/instrument/tests/test_stored_dict.py index 4d720d8..42981d3 100644 --- a/src/instrument/tests/test_stored_dict.py +++ b/src/instrument/tests/test_stored_dict.py @@ -9,7 +9,6 @@ import pytest -from ..utils.config_loaders import load_config_yaml from ..utils.stored_dict import StoredDict From f414b1e64587d3196a3b59ef39be1528ab485660 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 14:13:10 -0500 Subject: [PATCH 34/38] test wip --- src/instrument/tests/test_stored_dict.py | 128 ++++++++++++----------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/src/instrument/tests/test_stored_dict.py b/src/instrument/tests/test_stored_dict.py index 42981d3..739f7ac 100644 --- a/src/instrument/tests/test_stored_dict.py +++ b/src/instrument/tests/test_stored_dict.py @@ -9,6 +9,7 @@ import pytest +from ..utils.config_loaders import load_config_yaml from ..utils.stored_dict import StoredDict @@ -32,69 +33,70 @@ def md_file(): path.unlink() # delete the file -# def test_StoredDict(md_file): -# """Test the StoredDict class.""" -# assert md_file.exists() -# assert len(open(md_file).read().splitlines()) == 0 # empty - -# sdict = StoredDict(md_file, delay=0.2, title="unit testing") -# assert sdict is not None -# assert len(sdict) == 0 -# assert sdict._delay == 0.2 -# assert sdict._title == "unit testing" -# assert len(open(md_file).read().splitlines()) == 0 # still empty -# assert sdict._sync_key == f"sync_agent_{id(sdict):x}" -# assert not sdict.sync_in_progress - -# # Write an empty dictionary. -# sdict.flush() -# luftpause() -# buf = open(md_file).read().splitlines() -# assert len(buf) == 4, f"{buf=}" -# assert buf[-1] == "{}" # The empty dict. -# assert buf[0].startswith("# ") -# assert buf[1].startswith("# ") -# assert "unit testing" in buf[0] - -# # Add a new {key: value} pair. -# assert not sdict.sync_in_progress -# sdict["a"] = 1 -# assert sdict.sync_in_progress -# sdict.flush() -# assert time.time() > sdict._sync_deadline -# luftpause() -# assert not sdict.sync_in_progress -# assert len(open(md_file).read().splitlines()) == 4 - -# # Change the only value. -# sdict["a"] = 2 -# sdict.flush() -# luftpause() -# assert len(open(md_file).read().splitlines()) == 4 # Still. - -# # Add another key. -# sdict["bee"] = "bumble" -# sdict.flush() -# luftpause() -# assert len(open(md_file).read().splitlines()) == 5 - -# # Test _delayed_sync_to_storage. -# sdict["bee"] = "queen" -# md = load_config_yaml(md_file) -# assert len(md) == 2 # a & bee -# assert "a" in md -# assert md["bee"] == "bumble" # The old value. - -# time.sleep(sdict._delay / 2) -# # Still not written ... -# assert load_config_yaml(md_file)["bee"] == "bumble" - -# time.sleep(sdict._delay) -# # Should be written by now. -# assert load_config_yaml(md_file)["bee"] == "queen" - -# del sdict["bee"] # __delitem__ -# assert "bee" not in sdict # __getitem__ +def test_StoredDict(md_file): + """Test the StoredDict class.""" + assert md_file.exists() + assert len(open(md_file).read().splitlines()) == 0 # empty + + sdict = StoredDict(md_file, delay=0.2, title="unit testing") + assert sdict is not None + assert len(sdict) == 0 + assert sdict._delay == 0.2 + assert sdict._title == "unit testing" + assert len(open(md_file).read().splitlines()) == 0 # still empty + assert sdict._sync_key == f"sync_agent_{id(sdict):x}" + assert not sdict.sync_in_progress + + # Write an empty dictionary. + sdict.flush() + luftpause() + buf = open(md_file).read().splitlines() + assert len(buf) == 4, f"{buf=}" + assert buf[-1] == "{}" # The empty dict. + assert buf[0].startswith("# ") + assert buf[1].startswith("# ") + assert "unit testing" in buf[0] + + # Add a new {key: value} pair. + assert not sdict.sync_in_progress + sdict["a"] = 1 + assert sdict.sync_in_progress + sdict.flush() + assert time.time() > sdict._sync_deadline + luftpause() + assert not sdict.sync_in_progress + assert len(open(md_file).read().splitlines()) == 4 + + # Change the only value. + sdict["a"] = 2 + sdict.flush() + luftpause() + assert len(open(md_file).read().splitlines()) == 4 # Still. + + # Add another key. + sdict["bee"] = "bumble" + sdict.flush() + print(f"\n\nthis is the md_file: {md_file}\n\n") + luftpause() + assert len(open(md_file).read().splitlines()) == 5 + + # Test _delayed_sync_to_storage. + sdict["bee"] = "queen" + md = load_config_yaml(md_file) + assert len(md) == 2 # a & bee + assert "a" in md + assert md["bee"] == "bumble" # The old value. + + time.sleep(sdict._delay / 2) + # Still not written ... + assert load_config_yaml(md_file)["bee"] == "bumble" + + time.sleep(sdict._delay) + # Should be written by now. + assert load_config_yaml(md_file)["bee"] == "queen" + + del sdict["bee"] # __delitem__ + assert "bee" not in sdict # __getitem__ @pytest.mark.parametrize( From b3f7df3d88036fc49c0c99c537ffb1db5bb8f483 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 14:19:34 -0500 Subject: [PATCH 35/38] IS_ON = Enable --- src/instrument/callbacks/nexus_data_file_writer.py | 2 +- src/instrument/callbacks/spec_data_file_writer.py | 2 +- src/instrument/configs/iconfig.yml | 4 ++-- src/instrument/startup.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/instrument/callbacks/nexus_data_file_writer.py b/src/instrument/callbacks/nexus_data_file_writer.py index b8a6fd6..61108e1 100644 --- a/src/instrument/callbacks/nexus_data_file_writer.py +++ b/src/instrument/callbacks/nexus_data_file_writer.py @@ -48,7 +48,7 @@ def get_sample_title(self): nxwriter = MyNXWriter() # create the callback instance """The NeXus file writer object.""" -if iconfig.get("NEXUS_DATA_FILES", {}).get("IS_ON", False): +if iconfig.get("NEXUS_DATA_FILES", {}).get("ENABLE", False): RE.subscribe(nxwriter.receiver) # write data to NeXus files nxwriter.file_extension = iconfig.get("NEXUS_DATA_FILES", {}).get( diff --git a/src/instrument/callbacks/spec_data_file_writer.py b/src/instrument/callbacks/spec_data_file_writer.py index a628afd..bc46bbe 100644 --- a/src/instrument/callbacks/spec_data_file_writer.py +++ b/src/instrument/callbacks/spec_data_file_writer.py @@ -76,7 +76,7 @@ def newSpecFile(title, scan_id=None, RE=None): # make the SPEC file in current working directory (assumes is writable) specwriter.newfile(specwriter.spec_filename) -if iconfig.get("SPEC_DATA_FILES", {}).get("IS_ON", False): +if iconfig.get("SPEC_DATA_FILES", {}).get("ENABLE", False): RE.subscribe(specwriter.receiver) # write data to SPEC files logger.info("SPEC data file: %s", specwriter.spec_filename.resolve()) diff --git a/src/instrument/configs/iconfig.yml b/src/instrument/configs/iconfig.yml index 887d87d..38c40a9 100644 --- a/src/instrument/configs/iconfig.yml +++ b/src/instrument/configs/iconfig.yml @@ -46,12 +46,12 @@ BEC: ### Uncomment to use. If undefined, will not write that type of file. ### Each callback should apply its configuration from here. NEXUS_DATA_FILES: - IS_ON: false + ENABLE: false FILE_EXTENSION: hdf WARN_MISSING_CONTENT: true SPEC_DATA_FILES: - IS_ON: true + ENABLE: true FILE_EXTENSION: dat ### APS Data Management diff --git a/src/instrument/startup.py b/src/instrument/startup.py index e32325c..6c1816c 100644 --- a/src/instrument/startup.py +++ b/src/instrument/startup.py @@ -31,10 +31,10 @@ register_bluesky_magics() # Configure the session with callbacks, devices, and plans. -if iconfig.get("NEXUS_DATA_FILES", {}).get("IS_ON", False): +if iconfig.get("NEXUS_DATA_FILES", {}).get("ENABLE", False): from .callbacks.nexus_data_file_writer import nxwriter # noqa: F401 -if iconfig.get("SPEC_DATA_FILES", {}).get("IS_ON", False): +if iconfig.get("SPEC_DATA_FILES", {}).get("ENABLE", False): from .callbacks.spec_data_file_writer import newSpecFile # noqa: F401 from .callbacks.spec_data_file_writer import spec_comment # noqa: F401 from .callbacks.spec_data_file_writer import specwriter # noqa: F401 From 3b00fcc99899e1e0e40bd749e367b50d6d7d1298 Mon Sep 17 00:00:00 2001 From: Eric Codrea Date: Tue, 11 Mar 2025 14:25:29 -0500 Subject: [PATCH 36/38] cleaned up readme due to suggestions --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 331e050..893ccea 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ conda activate $ENV_NAME pip install -e ."[all]" ``` -## IPython console & Jupyter notebook +## IPython console Start To start the bluesky instrument session in a ipython execute the next command in a terminal: @@ -32,7 +32,7 @@ To start the bluesky instrument session in a ipython execute the next command in ipython ``` -## Jupyter Notebook +## Jupyter Notebook Start Start JupyterLab, a Jupyter notebook server, or a notebook, VSCode. ## Starting the BITS Package @@ -42,7 +42,7 @@ from instrument.startup import * RE(make_devices()) # create all the ophyd-style control devices ``` -## Running Sim Plan Demo +## Run Sim Plan Demo To run some simulated plans that ensure the installation worked as expected please run the next commands inside an ipython session or a jupyter notebook From 80c7200af737e6618b22b31c44059d756a8610f8 Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Tue, 11 Mar 2025 14:44:05 -0500 Subject: [PATCH 37/38] hide init_repo placeholders --- .github/workflows/init_repo.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/init_repo.yml b/.github/workflows/init_repo.yml index 29bd4b9..aba332d 100644 --- a/.github/workflows/init_repo.yml +++ b/.github/workflows/init_repo.yml @@ -16,14 +16,14 @@ jobs: with: python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e . + # - name: Install dependencies + # run: | + # python -m pip install --upgrade pip + # pip install -e . - - name: Run init_repo.sh - run: | - bash .github/workflows/init_repo.sh ${{ github.repository }} + # - name: Run init_repo.sh + # run: | + # bash .github/workflows/init_repo.sh ${{ github.repository }} - uses: stefanzweifel/git-auto-commit-action@v5 with: From 8f392ed471127991e7277877d70dbc08e8d74b75 Mon Sep 17 00:00:00 2001 From: Rafael Vescovi Date: Tue, 11 Mar 2025 14:47:46 -0500 Subject: [PATCH 38/38] add metadata --- src/bits/utils/metadata.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bits/utils/metadata.py b/src/bits/utils/metadata.py index 3f743eb..64305c5 100644 --- a/src/bits/utils/metadata.py +++ b/src/bits/utils/metadata.py @@ -28,7 +28,8 @@ import pysumreg import spec2nexus -from .config_loaders import iconfig +import bits +from bits.utils.config_loaders import iconfig logger = logging.getLogger(__name__) logger.bsdev(__file__) @@ -52,6 +53,7 @@ python=sys.version.split(" ")[0], pysumreg=pysumreg.__version__, spec2nexus=spec2nexus.__version__, + bits=bits.__version__, ) RE_CONFIG = iconfig.get("RUN_ENGINE", {})