diff --git a/.copier-answers.yml b/.copier-answers.yml
index e3d184b..031c161 100644
--- a/.copier-answers.yml
+++ b/.copier-answers.yml
@@ -1,5 +1,5 @@
# Changes here will be overwritten by Copier
-_commit: 2.3.0
+_commit: 2.5.0
_src_path: gh:diamondlightsource/python-copier-template
author_email: giles.knap@diamond.ac.uk
author_name: Giles Knap
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index c5e850a..1057d4c 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -24,4 +24,4 @@ It is recommended that developers use a [vscode devcontainer](https://code.visua
This project was created using the [Diamond Light Source Copier Template](https://github.com/DiamondLightSource/python-copier-template) for Python projects.
-For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.3.0/how-to.html).
+For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.5.0/how-to.html).
diff --git a/.github/pages/make_switcher.py b/.github/pages/make_switcher.py
index dbf8acc..c06813a 100755
--- a/.github/pages/make_switcher.py
+++ b/.github/pages/make_switcher.py
@@ -1,12 +1,14 @@
+"""Make switcher.json to allow docs to switch between different versions."""
+
import json
import logging
from argparse import ArgumentParser
from pathlib import Path
from subprocess import CalledProcessError, check_output
-from typing import Optional
def report_output(stdout: bytes, label: str) -> list[str]:
+ """Print and return something received frm stdout."""
ret = stdout.decode().strip().split("\n")
print(f"{label}: {ret}")
return ret
@@ -24,7 +26,7 @@ def get_sorted_tags_list() -> list[str]:
return report_output(stdout, "Tags list")
-def get_versions(ref: str, add: Optional[str]) -> list[str]:
+def get_versions(ref: str, add: str | None) -> list[str]:
"""Generate the file containing the list of all GitHub Pages builds."""
# Get the directories (i.e. builds) from the GitHub Pages branch
try:
@@ -53,14 +55,12 @@ def get_versions(ref: str, add: Optional[str]) -> list[str]:
return versions
-def write_json(path: Path, repository: str, versions: str):
+def write_json(path: Path, repository: str, versions: list[str]):
+ """Write the JSON switcher to path."""
org, repo_name = repository.split("/")
- pages_url = f"https://{org}.github.io"
- if repo_name != f"{org}.github.io":
- # Only add the repo name if it isn't the source for the org pages site
- pages_url += f"/{repo_name}"
struct = [
- {"version": version, "url": f"{pages_url}/{version}/"} for version in versions
+ {"version": version, "url": f"https://{org}.github.io/{repo_name}/{version}/"}
+ for version in versions
]
text = json.dumps(struct, indent=2)
print(f"JSON switcher:\n{text}")
@@ -68,6 +68,7 @@ def write_json(path: Path, repository: str, versions: str):
def main(args=None):
+ """Parse args and write switcher."""
parser = ArgumentParser(
description="Make a versions.json file from gh-pages directories"
)
diff --git a/.github/workflows/_pypi.yml b/.github/workflows/_pypi.yml
index 0c5258d..8032bba 100644
--- a/.github/workflows/_pypi.yml
+++ b/.github/workflows/_pypi.yml
@@ -15,3 +15,5 @@ jobs:
- name: Publish to PyPI using trusted publishing
uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ attestations: false
diff --git a/.github/workflows/_release.yml b/.github/workflows/_release.yml
index 10d8ed8..81b6264 100644
--- a/.github/workflows/_release.yml
+++ b/.github/workflows/_release.yml
@@ -23,7 +23,7 @@ jobs:
- name: Create GitHub Release
# We pin to the SHA, not the tag, for security reasons.
# https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions
- uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
+ uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9
with:
prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
files: "*"
diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index 36d8f50..0000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "name": "Debug Unit Test",
- "type": "debugpy",
- "request": "launch",
- "justMyCode": false,
- "program": "${file}",
- "purpose": [
- "debug-test"
- ],
- "console": "integratedTerminal",
- "env": {
- // Enable break on exception when debugging tests (see: tests/conftest.py)
- "PYTEST_RAISE": "1",
- },
- }
- ]
-}
diff --git a/Dockerfile b/Dockerfile
index c4404ec..35d2abf 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# The devcontainer should use the developer target and run as root with podman
# or docker with user namespaces.
ARG PYTHON_VERSION=3.11
-FROM python:${PYTHON_VERSION} as developer
+FROM python:${PYTHON_VERSION} AS developer
# Add any system dependencies for the developer/build environment here
RUN apt-get update && apt-get install -y --no-install-recommends \
diff --git a/README.md b/README.md
index cf97681..933dee4 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
[![CI](https://github.com/epics-containers/builder2ibek/actions/workflows/ci.yml/badge.svg)](https://github.com/epics-containers/builder2ibek/actions/workflows/ci.yml)
[![Coverage](https://codecov.io/gh/epics-containers/builder2ibek/branch/main/graph/badge.svg)](https://codecov.io/gh/epics-containers/builder2ibek)
[![PyPI](https://img.shields.io/pypi/v/builder2ibek.svg)](https://pypi.org/project/builder2ibek)
-[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
+[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
# builder2ibek
diff --git a/src/builder2ibek/__main__.py b/src/builder2ibek/__main__.py
index 250b4c9..2179568 100644
--- a/src/builder2ibek/__main__.py
+++ b/src/builder2ibek/__main__.py
@@ -88,6 +88,5 @@ def beamline(
raise typer.Exit(code=1)
-# test with: python -m builder2ibek
if __name__ == "__main__":
cli()
diff --git a/src/builder2ibek/convert.py b/src/builder2ibek/convert.py
index 2943e26..b1db250 100644
--- a/src/builder2ibek/convert.py
+++ b/src/builder2ibek/convert.py
@@ -5,7 +5,6 @@
from typing import Any
from builder2ibek.builder import Builder, Element
-from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.moduleinfos import module_infos
from builder2ibek.types import Entity, Generic_IOC
@@ -51,27 +50,23 @@ def do_one_element(element: Element, ioc: Generic_IOC):
# then dispatch to a specific handler if there is one
assert isinstance(element, Element)
- if element.module in module_infos:
- info = module_infos[element.module]
- entity.type = f"{info.yaml_component}.{element.name}"
-
- new_xml = globalHandler(entity, element.name, ioc, info.handler)
- if new_xml:
- handle_new_xml(new_xml, entity, ioc, info)
- else:
- new_xml = globalHandler(entity, element.name, ioc)
- if new_xml:
- handle_new_xml(new_xml, entity, ioc)
+ info = (
+ module_infos[element.module]
+ if element.module in module_infos
+ else module_infos["generic"]
+ )
-def handle_new_xml(new_xml: str, entity: Entity, ioc: Generic_IOC, info=None):
- new_builder = Builder()
- new_builder.load_string(new_xml)
- ioc.entities.remove(entity)
- do_dispatch(new_builder, ioc)
+ entity.type = f"{info.yaml_component}.{element.name}"
+ new_xml = info.handler(entity, element.name, ioc)
+ if new_xml:
+ new_builder = Builder()
+ new_builder.load_string(new_xml)
+ ioc.entities.remove(entity)
+ do_dispatch(new_builder, ioc)
if entity.is_deleted():
ioc.entities.remove(entity)
- if not entity.is_deleted() and info:
+ else:
add_defaults(entity, info.defaults)
diff --git a/src/builder2ibek/converters/ADAravis.py b/src/builder2ibek/converters/ADAravis.py
index 107888f..9c609ec 100644
--- a/src/builder2ibek/converters/ADAravis.py
+++ b/src/builder2ibek/converters/ADAravis.py
@@ -1,3 +1,4 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = ["aravisGigE", "ADAravis"]
@@ -5,6 +6,7 @@
yaml_component = "ADAravis"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
if entity_type == "aravisCamera":
entity.remove("PV_ALIAS")
diff --git a/src/builder2ibek/converters/ADZMQ.py b/src/builder2ibek/converters/ADZMQ.py
index 81c3ebd..7f02a48 100644
--- a/src/builder2ibek/converters/ADZMQ.py
+++ b/src/builder2ibek/converters/ADZMQ.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "ADZMQ"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pvlogging support module
diff --git a/src/builder2ibek/converters/BL20J.py b/src/builder2ibek/converters/BL20J.py
index 55c88b7..714b40a 100644
--- a/src/builder2ibek/converters/BL20J.py
+++ b/src/builder2ibek/converters/BL20J.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "BL20J"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pvlogging support module
diff --git a/src/builder2ibek/converters/BL46P-BUILDER.py b/src/builder2ibek/converters/BL46P-BUILDER.py
index 920ecb2..c6c6571 100644
--- a/src/builder2ibek/converters/BL46P-BUILDER.py
+++ b/src/builder2ibek/converters/BL46P-BUILDER.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "BL46P-BUILDER"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function
diff --git a/src/builder2ibek/converters/adUtil.py b/src/builder2ibek/converters/adUtil.py
index 2f3dfe9..bf196a7 100644
--- a/src/builder2ibek/converters/adUtil.py
+++ b/src/builder2ibek/converters/adUtil.py
@@ -4,6 +4,7 @@
import ruamel.yaml as yaml
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "adUtil"
@@ -13,6 +14,7 @@
GDA_PLUGINS = Path(__file__).parent / "gdaPlugins.yaml"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
if entity_type == "gdaPlugins":
ioc.entities.remove(entity)
diff --git a/src/builder2ibek/converters/autosave.py b/src/builder2ibek/converters/autosave.py
index b469d59..e4cceff 100644
--- a/src/builder2ibek/converters/autosave.py
+++ b/src/builder2ibek/converters/autosave.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "autosave"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pvlogging support module
diff --git a/src/builder2ibek/converters/deviocstats.py b/src/builder2ibek/converters/deviocstats.py
index e8dafdb..54cf133 100644
--- a/src/builder2ibek/converters/deviocstats.py
+++ b/src/builder2ibek/converters/deviocstats.py
@@ -1,3 +1,4 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "devIocStats"
@@ -12,6 +13,7 @@
}
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
if (
entity_type == "devIocStatsHelper" or entity_type == "iocAdminSoft"
diff --git a/src/builder2ibek/converters/digitelMpc.py b/src/builder2ibek/converters/digitelMpc.py
index d8e6ec7..6285666 100644
--- a/src/builder2ibek/converters/digitelMpc.py
+++ b/src/builder2ibek/converters/digitelMpc.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "digitelMpc"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pmac support module
diff --git a/src/builder2ibek/converters/dlsPLC.py b/src/builder2ibek/converters/dlsPLC.py
index 99223d1..6ebd561 100644
--- a/src/builder2ibek/converters/dlsPLC.py
+++ b/src/builder2ibek/converters/dlsPLC.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "dlsPLC"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pmac support module
diff --git a/src/builder2ibek/converters/epics_base.py b/src/builder2ibek/converters/epics_base.py
index 464f681..3ee93d7 100644
--- a/src/builder2ibek/converters/epics_base.py
+++ b/src/builder2ibek/converters/epics_base.py
@@ -1,9 +1,11 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "EPICS_BASE"
yaml_component = "epics"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
if entity_type == "EpicsEnvSet":
if entity["key"] == "EPICS_CA_MAX_ARRAY_BYTES":
diff --git a/src/builder2ibek/converters/ffmpegServer.py b/src/builder2ibek/converters/ffmpegServer.py
index e21ea0c..1a4f89b 100644
--- a/src/builder2ibek/converters/ffmpegServer.py
+++ b/src/builder2ibek/converters/ffmpegServer.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "ffmpegServer"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pvlogging support module
diff --git a/src/builder2ibek/converters/globalHandler.py b/src/builder2ibek/converters/globalHandler.py
index 1ce1aa3..8ba72ca 100644
--- a/src/builder2ibek/converters/globalHandler.py
+++ b/src/builder2ibek/converters/globalHandler.py
@@ -1,13 +1,23 @@
-from builder2ibek.types import Entity, Generic_IOC
+xml_component = "generic"
-xml_component = "global"
-
-def globalHandler(
- entity: Entity, entity_type: str, ioc: Generic_IOC, target_handler=None
-):
+def handler(entity, entity_type, ioc, realHandler=None):
+ """
+ Generic/global entity handler
+ """
entity.remove("gda_name")
entity.remove("gda_desc")
- if target_handler:
- return target_handler(entity, entity_type, ioc)
+ if realHandler:
+ return realHandler(entity, entity_type, ioc)
+ else:
+ return None
+
+
+def globalHandler(realHandler):
+ """
+ Decorator for generic/global handler
+ """
+ return lambda entity, entity_type, ioc: handler(
+ entity, entity_type, ioc, realHandler
+ )
diff --git a/src/builder2ibek/converters/mks937a.py b/src/builder2ibek/converters/mks937a.py
index daef4a2..34ab984 100644
--- a/src/builder2ibek/converters/mks937a.py
+++ b/src/builder2ibek/converters/mks937a.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "mks937a"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pmac support module
diff --git a/src/builder2ibek/converters/pmac.py b/src/builder2ibek/converters/pmac.py
index b4123a3..3308b9e 100644
--- a/src/builder2ibek/converters/pmac.py
+++ b/src/builder2ibek/converters/pmac.py
@@ -2,6 +2,7 @@
The convertor handler module for pmac support module
"""
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
# The prefix for Builder XML Tags that this support module uses
@@ -16,6 +17,7 @@
)
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pmac support module
diff --git a/src/builder2ibek/converters/pvlogging.py b/src/builder2ibek/converters/pvlogging.py
index 93186f1..2c23064 100644
--- a/src/builder2ibek/converters/pvlogging.py
+++ b/src/builder2ibek/converters/pvlogging.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "pvlogging"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pvlogging support module
diff --git a/src/builder2ibek/converters/records.py b/src/builder2ibek/converters/records.py
index b7dd77a..0a60ed9 100644
--- a/src/builder2ibek/converters/records.py
+++ b/src/builder2ibek/converters/records.py
@@ -1,8 +1,10 @@
+from builder2ibek.converters.globalHandler import globalHandler
from builder2ibek.types import Entity, Generic_IOC
xml_component = "records"
+@globalHandler
def handler(entity: Entity, entity_type: str, ioc: Generic_IOC):
"""
XML to YAML specialist convertor function for the pvlogging support module
diff --git a/tests/samples/BL45P-MO-IOC-01.xml b/tests/samples/BL45P-MO-IOC-01.xml
index 14cb66e..2ca672c 100644
--- a/tests/samples/BL45P-MO-IOC-01.xml
+++ b/tests/samples/BL45P-MO-IOC-01.xml
@@ -4,7 +4,7 @@
-
+
diff --git a/tests/samples/xml2yaml.yaml b/tests/samples/xml2yaml.yaml
new file mode 100644
index 0000000..d511517
--- /dev/null
+++ b/tests/samples/xml2yaml.yaml
@@ -0,0 +1,319 @@
+# yaml-language-server: $schema=/epics/ibek-defs/ioc.schema.json
+
+ioc_name: "{{ __utils__.get_env('IOC_NAME') }}"
+
+description: auto-generated by https://github.com/epics-containers/builder2ibek
+
+entities:
+
+ - type: epics.EpicsEnvSet
+ name: EPICS_TS_MIN_WEST
+ value: '0'
+
+ - type: devIocStats.iocAdminSoft
+ IOC: '{{ ioc_name | upper }}'
+
+ - type: epics.EpicsCaMaxArrayBytes
+ max_bytes: 6000000
+
+ - type: pmac.pmacAsynIPPort
+ IP: 172.23.59.21:1025
+ name: BRICK1port
+
+ - type: pmac.GeoBrick
+ P: BL45P-MO-STEP-01
+ name: BRICK1
+ pmacAsynPort: BRICK1port
+
+ - type: pmac.pmacDisableLimitsCheck
+ Axis: 2
+ Controller: BRICK1
+
+ - type: pmac.pmacDisableLimitsCheck
+ Axis: 3
+ Controller: BRICK1
+
+ - type: pmac.pmacDisableLimitsCheck
+ Axis: 4
+ Controller: BRICK1
+
+ - type: pmac.pmacDisableLimitsCheck
+ Axis: 5
+ Controller: BRICK1
+
+ - type: pmac.dls_pmac_asyn_motor
+ ACCL: 0.1
+ ADDR: 2
+ Controller: BRICK1
+ DESC: Theta top
+ DHLM: 10000000
+ DIR: Pos
+ DLLM: -10000000
+ EGU: deg
+ FOFF: Variable
+ HOMEVISSTR: Absolute Encoder
+ M: :THETA:TOP
+ MRES: 0.087890625
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 1
+ UEIP: No
+ VELO: 90
+ VMAX: '1080'
+
+ - type: pmac.dls_pmac_asyn_motor
+ ACCL: 0.1
+ ADDR: 3
+ Controller: BRICK1
+ DESC: Theta bottom
+ DHLM: 10000000
+ DIR: Pos
+ DLLM: -10000000
+ EGU: deg
+ FOFF: Variable
+ HOMEVISSTR: Absolute Encoder
+ M: :THETA:BOT
+ MRES: 0.087890625
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 1
+ UEIP: No
+ VELO: 90
+ VMAX: '1080'
+
+ - type: pmac.dls_pmac_asyn_motor
+ ADDR: 4
+ Controller: BRICK1
+ DESC: Hor EnDat chopper
+ DHLM: 10000000
+ DIR: Pos
+ DLLM: -10000000
+ EGU: deg
+ FOFF: Variable
+ HOMEVIS: 0
+ HOMEVISSTR: Absolute Encoder
+ M: :ENDAT
+ MRES: 4.3e-05
+ P: BL45P-MO-CHOP-01
+ PREC: 5
+ TWV: 1
+ UEIP: No
+ VELO: 43
+
+ - type: pmac.dls_pmac_asyn_motor
+ ACCL: 0.01
+ ADDR: 5
+ Controller: BRICK1
+ DESC: Ver Biss chopper
+ DHLM: 10000000
+ DIR: Pos
+ DLLM: -10000000
+ EGU: deg
+ ERES: ''
+ FOFF: Variable
+ HOMEVIS: 0
+ HOMEVISSTR: Absolute Encoder
+ M: :BISS
+ MRES: 0.000687
+ P: BL45P-MO-CHOP-01
+ PREC: 5
+ TWV: 1
+ UEIP: No
+ VELO: 720
+
+ - type: pmac.dls_pmac_asyn_motor
+ ADDR: 6
+ Controller: BRICK1
+ DESC: Sample X
+ DHLM: 19
+ DIR: Pos
+ DLLM: 0
+ EGU: mm
+ FOFF: Variable
+ M: :X
+ MRES: 0.0001
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 1
+ UEIP: No
+ VELO: 5
+
+ - type: pmac.dls_pmac_asyn_motor
+ ADDR: 7
+ Controller: BRICK1
+ DESC: Sample Y top
+ DHLM: 4
+ DIR: Pos
+ DLLM: -1
+ EGU: mm
+ FOFF: Variable
+ M: :Y:TOP
+ MRES: 0.0005
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 1
+ UEIP: No
+ VELO: 5
+
+ - type: pmac.dls_pmac_asyn_motor
+ ADDR: 8
+ Controller: BRICK1
+ DESC: Sample Y bottom
+ DHLM: 4
+ DIR: Pos
+ DLLM: -1
+ EGU: mm
+ FOFF: Variable
+ M: :Y:BOT
+ MRES: 0.0005
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 1
+ UEIP: No
+ VELO: 5
+
+ - type: pmac.autohome
+ GRP2: Theta
+ GRP3: X
+ GRP4: Y top
+ GRP5: Y bottom
+ P: BL45P-MO-STAGE-01
+ PLC: 10
+ PmacController: BRICK1
+
+ - type: pmac.CS
+ CS: 1
+ IdlePoll: 1000
+ PmacController: BRICK1
+ name: BRICK1.CS1
+
+ - type: pmac.CS
+ CS: 2
+ IdlePoll: 1000
+ PmacController: BRICK1
+ name: BRICK1.CS2
+
+ - type: pmac.CS
+ CS: 3
+ IdlePoll: 1000
+ PmacController: BRICK1
+ name: BRICK1.CS3
+
+ - type: pmac.dls_pmac_asyn_motor
+ ACCL: 0.1
+ ADDR: 7
+ Controller: BRICK1.CS2
+ DESC: Theta position
+ DHLM: 10000
+ DIR: Pos
+ DLLM: -10000
+ EGU: deg
+ FOFF: Variable
+ M: :THETA:POS
+ MRES: 0.0001
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 5
+ UEIP: No
+ VELO: 30
+ is_cs: true
+
+ - type: pmac.dls_pmac_asyn_motor
+ ACCL: 0.1
+ ADDR: 8
+ Controller: BRICK1.CS2
+ DESC: Theta skew
+ DHLM: 360
+ DIR: Pos
+ DLLM: -360
+ EGU: deg
+ FOFF: Variable
+ M: :THETA:SKEW
+ MRES: 0.0001
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 5
+ UEIP: No
+ VELO: 10
+ is_cs: true
+
+ - type: pmac.dls_pmac_asyn_motor
+ ADDR: 7
+ Controller: BRICK1.CS3
+ DESC: CS Y
+ DHLM: 4
+ DIR: Pos
+ DLLM: -1
+ EGU: mm
+ FOFF: Variable
+ M: :CS:Y
+ MRES: 0.0001
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 0.5
+ UEIP: No
+ VELO: 5
+ is_cs: true
+
+ - type: pmac.dls_pmac_asyn_motor
+ ADDR: 8
+ Controller: BRICK1.CS3
+ DESC: CS Y stretch
+ DHLM: 2
+ DIR: Pos
+ DLLM: -2
+ EGU: mm
+ FOFF: Variable
+ M: :CS:Y:STRETCH
+ MRES: 0.0001
+ P: BL45P-MO-STAGE-01
+ PREC: 3
+ TWV: 0.5
+ UEIP: No
+ VELO: 5
+ is_cs: true
+
+ - type: pmac.GeoBrickTrajectoryControl
+ NPOINTS: 500000
+ PmacController: BRICK1
+
+ - type: pmac.pmacCreateCsGroup
+ AxisCount: 9
+ Controller: BRICK1
+ GroupName: Default
+ GroupNumber: 0
+
+ - type: pmac.pmacCsGroupAddAxis
+ AxisDef: I
+ AxisNumber: 2
+ Controller: BRICK1
+ CoordSysNumber: 2
+ GroupNumber: 0
+
+ - type: pmac.pmacCsGroupAddAxis
+ AxisDef: I
+ AxisNumber: 3
+ Controller: BRICK1
+ CoordSysNumber: 2
+ GroupNumber: 0
+
+ - type: pmac.pmacCsGroupAddAxis
+ AxisDef: Z
+ AxisNumber: 6
+ Controller: BRICK1
+ CoordSysNumber: 3
+ GroupNumber: 0
+
+ - type: pmac.pmacCsGroupAddAxis
+ AxisDef: I
+ AxisNumber: 7
+ Controller: BRICK1
+ CoordSysNumber: 3
+ GroupNumber: 0
+
+ - type: pmac.pmacCsGroupAddAxis
+ AxisDef: I
+ AxisNumber: 8
+ Controller: BRICK1
+ CoordSysNumber: 3
+ GroupNumber: 0