diff --git a/.gitattributes b/.gitattributes index fd2328b..c59ad05 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -# slac_db/yaml/* linguist-generated -# slac_db/package_data/* linguist-generated -# tests/test_data/* linguist-generated -# tests/test_data/lcls-tools-yaml/* linguist-generated +slac_db/yaml/* linguist-generated +slac_db/package_data/* linguist-generated +tests/test_data/* linguist-generated +*.sqlite3 filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5cfcca9..c35d527 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,6 +22,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + lfs: true - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -36,4 +38,4 @@ jobs: run: | echo -e '## Test results\n\n```' >> "$GITHUB_STEP_SUMMARY" python -m unittest discover -s tests -v 2>&1 | tee -a "$GITHUB_STEP_SUMMARY" - echo '```' >> "$GITHUB_STEP_SUMMARY" \ No newline at end of file + echo '```' >> "$GITHUB_STEP_SUMMARY" diff --git a/pyproject.toml b/pyproject.toml index 71aeaac..84ccd1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,8 +15,9 @@ classifiers = [ ] dependencies = [ "numpy", - "pyyaml", "pyepics", + "pykern", + "pyyaml", ] description = "Tools to support high level application development at LCLS using Python" dynamic = ["version"] diff --git a/slac_db/__init__.py b/slac_db/__init__.py index 696b588..abf8090 100644 --- a/slac_db/__init__.py +++ b/slac_db/__init__.py @@ -18,7 +18,7 @@ def _flatten(nested_list): return _flatten(nested_list[0]) + _flatten(nested_list[1:]) return nested_list[:1] + _flatten(nested_list[1:]) beampath_definition_file = os.path.join( - slac_db.package_data(), "beampaths.yaml" + slac_db.config.package_data(), "beampaths.yaml" ) with open(beampath_definition_file, "r") as file: beampath_definitions = yaml.safe_load(file) diff --git a/slac_db/create/__init__.py b/slac_db/create/__init__.py new file mode 100644 index 0000000..844c95e --- /dev/null +++ b/slac_db/create/__init__.py @@ -0,0 +1,8 @@ +import slac_db.create.meme_names +import slac_db.create.lcls_elements_csv + +def oracle_db(csv_source=None): + slac_db.create.lcls_elements_csv.to_oracle_db(csv_source) + +def aida_db(): + slac_db.create.meme_names.to_aida_db() diff --git a/slac_db/create/lcls_elements_csv.py b/slac_db/create/lcls_elements_csv.py new file mode 100644 index 0000000..06e3bcd --- /dev/null +++ b/slac_db/create/lcls_elements_csv.py @@ -0,0 +1,26 @@ +import csv +import slac_db.config +import slac_db.oracle + +def to_oracle_db(csv_source=None): + p = _Parser(csv_source=csv_source) + return slac_db.oracle.recreate(p) + +class _Parser(): + def __init__(self, csv_source=None): + if not csv_source: + csv_source = ( + slac_db.config.package_data() / "lcls_elements.csv" + ) + self.rows = {} + with open(csv_source, "r") as c: + reader = csv.reader(c) + self._parse_csv(reader) + + def _parse_csv(self, reader): + names = [r.lower() for r in next(reader)] + i = 0 + for row in reader: + values = [None if v == '' else v for v in row] + self.rows[i] = dict(zip(names, values)) + i += 1 diff --git a/slac_db/create/meme_names.py b/slac_db/create/meme_names.py new file mode 100644 index 0000000..35ba2b2 --- /dev/null +++ b/slac_db/create/meme_names.py @@ -0,0 +1,15 @@ +import slac_db.directory_service + +def to_directory_service_db(): + return slac_db.directory_service.recreate(_Parser()) + +class _Parser: + def __init__(self): + self.addresses = set() + self._get_from_meme() + + def _get_from_meme(self): + import meme.names + address_list = meme.names.list_pvs("%", timeout=600) + for a in address_list: + self.addresses.add(a) diff --git a/slac_db/directory_service.py b/slac_db/directory_service.py new file mode 100644 index 0000000..8c8ac1d --- /dev/null +++ b/slac_db/directory_service.py @@ -0,0 +1,100 @@ +import os +import os.path +import slac_db.config +import sqlalchemy +import pykern.sql_db +import slac_db.oracle + +_meta = None + +def get_addresses(device=None): + """Get all addresses per device. + + Args: + device (str): MAD name of the device as found in Oracle. + + Returns: + tuple: Sorted address values. + """ + head = slac_db.oracle.get_address_header(device=device) + with _session() as s: + cs_address = s.t.addresses.c["address"] + return tuple(sorted( + r["address"] for r in s.select( + sqlalchemy.select( + cs_address + ).where( + cs_address.like(f"{head}%") + ) + ) + )) + +def recreate(parser): + """Rebuild the local directory_service sqlite3 database + only if it is not already loaded. + + Args: + parser: Container for column data. + """ + assert not _meta + assert parser.addresses + if os.path.exists(_directory_service_uri()): + os.remove(_directory_service_uri()) + _Inserter(parser) + +class _Inserter: + """Creates a session and commits rows to the db. + + Functions: + _addresses: Inserts all addresses in parser.addresses. + """ + def __init__(self, parser): + self.counts = {"addresses": 0} + with _session() as s: + self._addresses(parser.addresses, s) + + def _addresses(self, addresses, session): + # We have to do it this way unfortunately. + # Bulk insert is not faster. + n = len(addresses) + i = 0 + for a in addresses: + session.insert("addresses", address=a) + i += 1 + print("{i} / {n}", end='\r') + +def _db_type_prefix(uri): + if not uri.startswith("sqlite"): + uri = 'sqlite:///' + uri + return uri + +def _init_db(uri=None): + """Initializes pykern sqlalchemy wrapper. Initialization + occurs when a session is first created. + + _meta: wrapper that holds sqlalchemy metadata. + """ + global _meta + if uri is None: + uri = _directory_service_uri() + uri = _db_type_prefix(uri) + schema = { + "addresses": { + "address": "str 64 primary_key", + } + } + _meta = pykern.sql_db.Meta( + uri=uri, + schema=schema + ) + +def _directory_service_uri(): + uri = ( + slac_db.config.package_data() / 'directory_service_pvs.sqlite3' + ) + return str(uri) + +def _session(): + if _meta is None: + _init_db() + return _meta.session() diff --git a/slac_db/oracle.py b/slac_db/oracle.py new file mode 100644 index 0000000..b9d82fe --- /dev/null +++ b/slac_db/oracle.py @@ -0,0 +1,191 @@ +import slac_db.config +import sqlalchemy +import pykern.sql_db +import os.path +import os + + +_meta = None + +def get_address_header(device=None): + """Get address header of a device. + + Args: + device (str): MAD name of the device as found in Oracle. + + Returns: + tuple: The address header. + """ + with _session() as s: + return s.select_one( + sqlalchemy.select( + s.t.elements.c["control system name"] + ).where( + s.t.elements.c["element"] == device + ) + )["control system name"] + +def get_devices(area=None, device_type=None): + """Get devices of one type from an area. + + Args: + area (str): Name of the accelerator area. + device_type (str): Type of device as listed in Oracle. + + Returns: + tuple: Device names in Z order. + """ + if device_type is None: + device_type = "%" + with _session() as s: + return tuple( + r.element for r in s.select( + sqlalchemy.select( + s.t.elements.c["element"] + ).where( + s.t.elements.c["keyword"] == device_type + ).where( + s.t.elements.c["area"] == area + ).order_by(s.t.elements.c["suml (m)"]) + ) + ) + +def get_device_row(element=None): + """Get the full row for an element. + + Args: + element: name of the element to get. + Returns: + sql alchemy row: Row object with each column. + """ + with _session() as s: + return s.select_one( + sqlalchemy.select( + s.t.elements + ).where( + s.t.elements.c["element"] == element + ) + ) + +def get_beampaths(): + """Get all beampaths from Oracle. + + Returns: + List of beampaths sorted alphabetically. + """ + beampaths = set() + def parse_beampaths(beampath_csv): + if beampath_csv is None: + return + c = beampath_csv.replace(' ', '').split(',') + c = filter(None, c) + beampaths.update(c) + + with _session() as s: + query = sqlalchemy.select(s.t.elements.c.beampath).distinct() + for r in s.select(query): + parse_beampaths(r.beampath) + return sorted(list(beampaths)) + + +def get_areas(): + """Get all areas from Oracle. + + Returns: + List of areas sorted alphabetically. + """ + def exclude_bad_patterns(column): + bad_patterns = ['\t- NO AREA -', '*%'] + filters = [None] * len(bad_patterns) + for i in range(0, len(bad_patterns)): + filters[i] = column.not_like(bad_patterns[i]) + return sqlalchemy.and_(*filters) + + with _session() as s: + return list( + r.area for r in s.select( + sqlalchemy.select( + s.t.elements.c.area + ).where( + exclude_bad_patterns(s.t.elements.c.area) + ).distinct() + ) + ) + +def recreate(parser): + """Rebuilds the sqlite copy of Oracle. + Fails if a connection has already been made. + + Args: + Parser object with attribute 'rows' for row data. + """ + if _meta: + raise AssertionError( + "Database connnection already initialized. " + + "Restart Python interpreter." + ) + if not hasattr(parser, "rows"): + raise AssertionError( + "Parser is missing attribute 'rows'. " + ) + if os.path.exists(_oracle_uri()): + os.remove(_oracle_uri()) + _Inserter(parser) + + +class _Inserter: + """Inserts rows into sqllite database. + """ + def __init__(self, parser): + with _session() as s: + self._rows(parser.rows, s) + def _rows(self, rows, session): + for r in rows.values(): + ins = {} + for c in session.t.elements.c: + ins[c.name] = r[c.name] + session.insert("elements", **ins) + + +def _db_type_prefix(uri): + if not uri.startswith("sqlite"): + uri = 'sqlite:///' + uri + return uri + +def _init_db(uri=None): + """Initializes pykern sqlalchemy wrapper. Initialization + occurs when a session is first created. + + _meta: wrapper that holds sqlalchemy metadata. + """ + global _meta + if uri is None: + uri = _oracle_uri() + uri = _db_type_prefix(uri) + schema = { + "elements": { + "Area": "str 64 nullable", + "Element": "str 64 primary_key", + "Control System Name": "str 64 nullable", + "Keyword": "str 64 nullable", + "Beampath": "str 64 nullable", + "SumL (m)": "float 64 nullable", + "Effective Length (m)": "float 64 nullable", + "Rf Frequency (MHz)": "float 64 nullable" + } + } + _meta = pykern.sql_db.Meta( + uri=uri, + schema=schema + ) + +def _oracle_uri(): + uri = ( + slac_db.config.package_data() / 'lcls_elements.sqlite3' + ) + return str(uri) + +def _session(): + if _meta is None: + _init_db() + return _meta.session() diff --git a/slac_db/package_data/directory_service_pvs.sqlite3 b/slac_db/package_data/directory_service_pvs.sqlite3 new file mode 100644 index 0000000..8387c27 --- /dev/null +++ b/slac_db/package_data/directory_service_pvs.sqlite3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8dea92bee30e1e0327eaf53ceb0b182737f6d474bbed502bd57e31f37f371672 +size 548225024 diff --git a/slac_db/package_data/lcls_elements.sqlite3 b/slac_db/package_data/lcls_elements.sqlite3 new file mode 100644 index 0000000..5e7c2c1 --- /dev/null +++ b/slac_db/package_data/lcls_elements.sqlite3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61078084eb2d40e8d833d26ec4eeaa7402d2037a01a77a1f629498efbb85f3f7 +size 704512 diff --git a/tests/test_data/DIAG0.yaml b/tests/test_data/DIAG0.yaml new file mode 100644 index 0000000..1ca6683 --- /dev/null +++ b/tests/test_data/DIAG0.yaml @@ -0,0 +1,754 @@ +bpms: + BPMDG000?: + controls_information: + PVs: {} + control_name: BPMS:DIAG0:136 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 43.879 + type: BPM + BPMDG001: + controls_information: + PVs: + tmit: BPMS:DIAG0:190:TMIT + x: BPMS:DIAG0:190:X + y: BPMS:DIAG0:190:Y + control_name: BPMS:DIAG0:190 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 46.232 + type: BPM + BPMDG002: + controls_information: + PVs: + tmit: BPMS:DIAG0:210:TMIT + x: BPMS:DIAG0:210:X + y: BPMS:DIAG0:210:Y + control_name: BPMS:DIAG0:210 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 47.094 + type: BPM + BPMDG003: + controls_information: + PVs: + tmit: BPMS:DIAG0:230:TMIT + x: BPMS:DIAG0:230:X + y: BPMS:DIAG0:230:Y + control_name: BPMS:DIAG0:230 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 47.956 + type: BPM + BPMDG004: + controls_information: + PVs: + tmit: BPMS:DIAG0:270:TMIT + x: BPMS:DIAG0:270:X + y: BPMS:DIAG0:270:Y + control_name: BPMS:DIAG0:270 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 50.114 + type: BPM + BPMDG005: + controls_information: + PVs: + tmit: BPMS:DIAG0:285:TMIT + x: BPMS:DIAG0:285:X + y: BPMS:DIAG0:285:Y + control_name: BPMS:DIAG0:285 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 50.763 + type: BPM + BPMDG008: + controls_information: + PVs: + tmit: BPMS:DIAG0:370:TMIT + x: BPMS:DIAG0:370:X + y: BPMS:DIAG0:370:Y + control_name: BPMS:DIAG0:370 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 54.763 + type: BPM + BPMDG009: + controls_information: + PVs: + tmit: BPMS:DIAG0:390:TMIT + x: BPMS:DIAG0:390:X + y: BPMS:DIAG0:390:Y + control_name: BPMS:DIAG0:390 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 55.463 + type: BPM + BPMDG011: + controls_information: + PVs: + tmit: BPMS:DIAG0:470:TMIT + x: BPMS:DIAG0:470:X + y: BPMS:DIAG0:470:Y + control_name: BPMS:DIAG0:470 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 59.233 + type: BPM + BPMDG012: + controls_information: + PVs: + tmit: BPMS:DIAG0:520:TMIT + x: BPMS:DIAG0:520:X + y: BPMS:DIAG0:520:Y + control_name: BPMS:DIAG0:520 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 61.631 + type: BPM + BPMDG0RF: + controls_information: + PVs: + tmit: BPMS:DIAG0:330:TMIT + x: BPMS:DIAG0:330:X + y: BPMS:DIAG0:330:Y + control_name: BPMS:DIAG0:330 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 52.74 + type: BPM +magnets: + BKRDG0: + controls_information: + PVs: + bact: KICK:DIAG0:110:BACT + bcon: KICK:DIAG0:110:BCON + bctrl: KICK:DIAG0:110:BCTRL + bdes: KICK:DIAG0:110:BDES + bmax: KICK:DIAG0:110:BMAX + bmin: KICK:DIAG0:110:BMIN + ctrl: KICK:DIAG0:110:CTRL + control_name: KICK:DIAG0:110 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 1.0 + sum_l_meters: 42.73 + type: BEND + BLRDG0: + controls_information: + PVs: + bact: BEND:DIAG0:155:BACT + bcon: BEND:DIAG0:155:BCON + bctrl: BEND:DIAG0:155:BCTRL + bdes: BEND:DIAG0:155:BDES + bmax: BEND:DIAG0:155:BMAX + bmin: BEND:DIAG0:155:BMIN + ctrl: BEND:DIAG0:155:CTRL + control_name: BEND:DIAG0:155 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.401 + sum_l_meters: 44.73 + type: BEND + BXDG0: + controls_information: + PVs: + bact: BEND:DIAG0:260:BACT + bcon: BEND:DIAG0:260:BCON + bctrl: BEND:DIAG0:260:BCTRL + bdes: BEND:DIAG0:260:BDES + bmax: BEND:DIAG0:260:BMAX + bmin: BEND:DIAG0:260:BMIN + ctrl: BEND:DIAG0:260:CTRL + control_name: BEND:DIAG0:260 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.4 + sum_l_meters: 49.486 + type: BEND + BYDG0: + controls_information: + PVs: + bact: BEND:DIAG0:510:BACT + bcon: BEND:DIAG0:510:BCON + bctrl: BEND:DIAG0:510:BCTRL + bdes: BEND:DIAG0:510:BDES + bmax: BEND:DIAG0:510:BMAX + bmin: BEND:DIAG0:510:BMIN + ctrl: BEND:DIAG0:510:CTRL + control_name: BEND:DIAG0:510 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.543 + sum_l_meters: 60.964 + type: BEND + QDG001: + controls_information: + PVs: + bact: QUAD:DIAG0:190:BACT + bcon: QUAD:DIAG0:190:BCON + bctrl: QUAD:DIAG0:190:BCTRL + bdes: QUAD:DIAG0:190:BDES + bmax: QUAD:DIAG0:190:BMAX + bmin: QUAD:DIAG0:190:BMIN + ctrl: QUAD:DIAG0:190:CTRL + control_name: QUAD:DIAG0:190 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.197 + sum_l_meters: 46.232 + type: QUAD + QDG002: + controls_information: + PVs: + bact: QUAD:DIAG0:210:BACT + bcon: QUAD:DIAG0:210:BCON + bctrl: QUAD:DIAG0:210:BCTRL + bdes: QUAD:DIAG0:210:BDES + bmax: QUAD:DIAG0:210:BMAX + bmin: QUAD:DIAG0:210:BMIN + ctrl: QUAD:DIAG0:210:CTRL + control_name: QUAD:DIAG0:210 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.197 + sum_l_meters: 47.094 + type: QUAD + QDG003: + controls_information: + PVs: + bact: QUAD:DIAG0:230:BACT + bcon: QUAD:DIAG0:230:BCON + bctrl: QUAD:DIAG0:230:BCTRL + bdes: QUAD:DIAG0:230:BDES + bmax: QUAD:DIAG0:230:BMAX + bmin: QUAD:DIAG0:230:BMIN + ctrl: QUAD:DIAG0:230:CTRL + control_name: QUAD:DIAG0:230 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.197 + sum_l_meters: 47.956 + type: QUAD + QDG004: + controls_information: + PVs: + bact: QUAD:DIAG0:270:BACT + bcon: QUAD:DIAG0:270:BCON + bctrl: QUAD:DIAG0:270:BCTRL + bdes: QUAD:DIAG0:270:BDES + bmax: QUAD:DIAG0:270:BMAX + bmin: QUAD:DIAG0:270:BMIN + ctrl: QUAD:DIAG0:270:CTRL + control_name: QUAD:DIAG0:270 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.108 + sum_l_meters: 50.114 + type: QUAD + QDG005: + controls_information: + PVs: + bact: QUAD:DIAG0:285:BACT + bcon: QUAD:DIAG0:285:BCON + bctrl: QUAD:DIAG0:285:BCTRL + bdes: QUAD:DIAG0:285:BDES + bmax: QUAD:DIAG0:285:BMAX + bmin: QUAD:DIAG0:285:BMIN + ctrl: QUAD:DIAG0:285:CTRL + control_name: QUAD:DIAG0:285 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.108 + sum_l_meters: 50.763 + type: QUAD + QDG006: + controls_information: + PVs: + bact: QUAD:DIAG0:300:BACT + bcon: QUAD:DIAG0:300:BCON + bctrl: QUAD:DIAG0:300:BCTRL + bdes: QUAD:DIAG0:300:BDES + bmax: QUAD:DIAG0:300:BMAX + bmin: QUAD:DIAG0:300:BMIN + ctrl: QUAD:DIAG0:300:CTRL + control_name: QUAD:DIAG0:300 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.108 + sum_l_meters: 51.463 + type: QUAD + QDG007: + controls_information: + PVs: + bact: QUAD:DIAG0:360:BACT + bcon: QUAD:DIAG0:360:BCON + bctrl: QUAD:DIAG0:360:BCTRL + bdes: QUAD:DIAG0:360:BDES + bmax: QUAD:DIAG0:360:BMAX + bmin: QUAD:DIAG0:360:BMIN + ctrl: QUAD:DIAG0:360:CTRL + control_name: QUAD:DIAG0:360 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.108 + sum_l_meters: 54.063 + type: QUAD + QDG008: + controls_information: + PVs: + bact: QUAD:DIAG0:370:BACT + bcon: QUAD:DIAG0:370:BCON + bctrl: QUAD:DIAG0:370:BCTRL + bdes: QUAD:DIAG0:370:BDES + bmax: QUAD:DIAG0:370:BMAX + bmin: QUAD:DIAG0:370:BMIN + ctrl: QUAD:DIAG0:370:CTRL + control_name: QUAD:DIAG0:370 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.108 + sum_l_meters: 54.763 + type: QUAD + QDG009: + controls_information: + PVs: + bact: QUAD:DIAG0:390:BACT + bcon: QUAD:DIAG0:390:BCON + bctrl: QUAD:DIAG0:390:BCTRL + bdes: QUAD:DIAG0:390:BDES + bmax: QUAD:DIAG0:390:BMAX + bmin: QUAD:DIAG0:390:BMIN + ctrl: QUAD:DIAG0:390:CTRL + control_name: QUAD:DIAG0:390 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.108 + sum_l_meters: 55.463 + type: QUAD + QDG010: + controls_information: + PVs: + bact: QUAD:DIAG0:455:BACT + bcon: QUAD:DIAG0:455:BCON + bctrl: QUAD:DIAG0:455:BCTRL + bdes: QUAD:DIAG0:455:BDES + bmax: QUAD:DIAG0:455:BMAX + bmin: QUAD:DIAG0:455:BMIN + ctrl: QUAD:DIAG0:455:CTRL + control_name: QUAD:DIAG0:455 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.108 + sum_l_meters: 58.533 + type: QUAD + QDG011: + controls_information: + PVs: + bact: QUAD:DIAG0:470:BACT + bcon: QUAD:DIAG0:470:BCON + bctrl: QUAD:DIAG0:470:BCTRL + bdes: QUAD:DIAG0:470:BDES + bmax: QUAD:DIAG0:470:BMAX + bmin: QUAD:DIAG0:470:BMIN + ctrl: QUAD:DIAG0:470:CTRL + control_name: QUAD:DIAG0:470 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.108 + sum_l_meters: 59.233 + type: QUAD + XCDG001: + controls_information: + PVs: + bact: XCOR:DIAG0:178:BACT + bcon: XCOR:DIAG0:178:BCON + bctrl: XCOR:DIAG0:178:BCTRL + bdes: XCOR:DIAG0:178:BDES + bmax: XCOR:DIAG0:178:BMAX + bmin: XCOR:DIAG0:178:BMIN + ctrl: XCOR:DIAG0:178:CTRL + control_name: XCOR:DIAG0:178 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 45.883 + type: XCOR + XCDG002: + controls_information: + PVs: + bact: XCOR:DIAG0:218:BACT + bcon: XCOR:DIAG0:218:BCON + bctrl: XCOR:DIAG0:218:BCTRL + bdes: XCOR:DIAG0:218:BDES + bmax: XCOR:DIAG0:218:BMAX + bmin: XCOR:DIAG0:218:BMIN + ctrl: XCOR:DIAG0:218:CTRL + control_name: XCOR:DIAG0:218 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 47.607 + type: XCOR + XCDG003: + controls_information: + PVs: + bact: XCOR:DIAG0:280:BACT + bcon: XCOR:DIAG0:280:BCON + bctrl: XCOR:DIAG0:280:BCTRL + bdes: XCOR:DIAG0:280:BDES + bmax: XCOR:DIAG0:280:BMAX + bmin: XCOR:DIAG0:280:BMIN + ctrl: XCOR:DIAG0:280:CTRL + control_name: XCOR:DIAG0:280 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 50.439 + type: XCOR + XCDG005: + controls_information: + PVs: + bact: XCOR:DIAG0:290:BACT + bcon: XCOR:DIAG0:290:BCON + bctrl: XCOR:DIAG0:290:BCTRL + bdes: XCOR:DIAG0:290:BDES + bmax: XCOR:DIAG0:290:BMAX + bmin: XCOR:DIAG0:290:BMIN + ctrl: XCOR:DIAG0:290:CTRL + control_name: XCOR:DIAG0:290 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 51.113 + type: XCOR + XCDG008: + controls_information: + PVs: + bact: XCOR:DIAG0:380:BACT + bcon: XCOR:DIAG0:380:BCON + bctrl: XCOR:DIAG0:380:BCTRL + bdes: XCOR:DIAG0:380:BDES + bmax: XCOR:DIAG0:380:BMAX + bmin: XCOR:DIAG0:380:BMIN + ctrl: XCOR:DIAG0:380:CTRL + control_name: XCOR:DIAG0:380 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 55.113 + type: XCOR + XCDG010: + controls_information: + PVs: + bact: XCOR:DIAG0:460:BACT + bcon: XCOR:DIAG0:460:BCON + bctrl: XCOR:DIAG0:460:BCTRL + bdes: XCOR:DIAG0:460:BDES + bmax: XCOR:DIAG0:460:BMAX + bmin: XCOR:DIAG0:460:BMIN + ctrl: XCOR:DIAG0:460:CTRL + control_name: XCOR:DIAG0:460 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 58.883 + type: XCOR + YCDG001: + controls_information: + PVs: + bact: YCOR:DIAG0:199:BACT + bcon: YCOR:DIAG0:199:BCON + bctrl: YCOR:DIAG0:199:BCTRL + bdes: YCOR:DIAG0:199:BDES + bmax: YCOR:DIAG0:199:BMAX + bmin: YCOR:DIAG0:199:BMIN + ctrl: YCOR:DIAG0:199:CTRL + control_name: YCOR:DIAG0:199 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 46.745 + type: YCOR + YCDG002: + controls_information: + PVs: + bact: YCOR:DIAG0:247:BACT + bcon: YCOR:DIAG0:247:BCON + bctrl: YCOR:DIAG0:247:BCTRL + bdes: YCOR:DIAG0:247:BDES + bmax: YCOR:DIAG0:247:BMAX + bmin: YCOR:DIAG0:247:BMIN + ctrl: YCOR:DIAG0:247:CTRL + control_name: YCOR:DIAG0:247 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 48.832 + type: YCOR + YCDG003: + controls_information: + PVs: + bact: YCOR:DIAG0:280:BACT + bcon: YCOR:DIAG0:280:BCON + bctrl: YCOR:DIAG0:280:BCTRL + bdes: YCOR:DIAG0:280:BDES + bmax: YCOR:DIAG0:280:BMAX + bmin: YCOR:DIAG0:280:BMIN + ctrl: YCOR:DIAG0:280:CTRL + control_name: YCOR:DIAG0:280 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 50.439 + type: YCOR + YCDG005: + controls_information: + PVs: + bact: YCOR:DIAG0:290:BACT + bcon: YCOR:DIAG0:290:BCON + bctrl: YCOR:DIAG0:290:BCTRL + bdes: YCOR:DIAG0:290:BDES + bmax: YCOR:DIAG0:290:BMAX + bmin: YCOR:DIAG0:290:BMIN + ctrl: YCOR:DIAG0:290:CTRL + control_name: YCOR:DIAG0:290 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 51.113 + type: YCOR + YCDG008: + controls_information: + PVs: + bact: YCOR:DIAG0:380:BACT + bcon: YCOR:DIAG0:380:BCON + bctrl: YCOR:DIAG0:380:BCTRL + bdes: YCOR:DIAG0:380:BDES + bmax: YCOR:DIAG0:380:BMAX + bmin: YCOR:DIAG0:380:BMIN + ctrl: YCOR:DIAG0:380:CTRL + control_name: YCOR:DIAG0:380 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 55.113 + type: YCOR + YCDG010: + controls_information: + PVs: + bact: YCOR:DIAG0:460:BACT + bcon: YCOR:DIAG0:460:BCON + bctrl: YCOR:DIAG0:460:BCTRL + bdes: YCOR:DIAG0:460:BDES + bmax: YCOR:DIAG0:460:BMAX + bmin: YCOR:DIAG0:460:BMIN + ctrl: YCOR:DIAG0:460:CTRL + control_name: YCOR:DIAG0:460 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.0 + sum_l_meters: 58.883 + type: YCOR +screens: + OTRDG02: + controls_information: + PVs: + filter_1_control: OTRS:DIAG0:420:FLT1_CTRL + filter_1_status: OTRS:DIAG0:420:FLT1_STS + filter_2_control: OTRS:DIAG0:420:FLT2_CTRL + filter_2_status: OTRS:DIAG0:420:FLT2_STS + image: OTRS:DIAG0:420:Image:ArrayData + lamp_power: OTRS:DIAG0:420:TGT_LAMP_PWR + n_bits: OTRS:DIAG0:420:N_OF_BITS + n_col: OTRS:DIAG0:420:Image:ArraySize1_RBV + n_row: OTRS:DIAG0:420:Image:ArraySize0_RBV + orient_x: OTRS:DIAG0:420:X_ORIENT + orient_y: OTRS:DIAG0:420:Y_ORIENT + ref_rate: OTRS:DIAG0:420:ArrayRate_RBV + ref_rate_vme: OTRS:DIAG0:420:FRAME_RATE + resolution: OTRS:DIAG0:420:RESOLUTION + sys_type: OTRS:DIAG0:420:SYS_TYPE + target_control: OTRS:DIAG0:420:PNEUMATIC + target_status: OTRS:DIAG0:420:TGT_STS + control_name: OTRS:DIAG0:420 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 56.813 + type: PROF + OTRDG04: + controls_information: + PVs: + filter_1_control: OTRS:DIAG0:525:FLT1_CTRL + filter_1_status: OTRS:DIAG0:525:FLT1_STS + filter_2_control: OTRS:DIAG0:525:FLT2_CTRL + filter_2_status: OTRS:DIAG0:525:FLT2_STS + image: OTRS:DIAG0:525:Image:ArrayData + lamp_power: OTRS:DIAG0:525:TGT_LAMP_PWR + n_bits: OTRS:DIAG0:525:N_OF_BITS + n_col: OTRS:DIAG0:525:Image:ArraySize1_RBV + n_row: OTRS:DIAG0:525:Image:ArraySize0_RBV + orient_x: OTRS:DIAG0:525:X_ORIENT + orient_y: OTRS:DIAG0:525:Y_ORIENT + ref_rate: OTRS:DIAG0:525:ArrayRate_RBV + ref_rate_vme: OTRS:DIAG0:525:FRAME_RATE + resolution: OTRS:DIAG0:525:RESOLUTION + sys_type: OTRS:DIAG0:525:SYS_TYPE + target_control: OTRS:DIAG0:525:PNEUMATIC + target_status: OTRS:DIAG0:525:TGT_STS + control_name: OTRS:DIAG0:525 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + sum_l_meters: 61.871 + type: PROF +tcavs: + TCXDG0: + controls_information: + PVs: + amplitude: TCAV:DIAG0:11:AREQ + amplitude_fbenb: TCAV:DIAG0:11:AFBENB + amplitude_fbst: TCAV:DIAG0:11:AFBST + amplitude_wocho: TCAV:DIAG0:11:AMPL_W0CH0 + mode_config: TCAV:DIAG0:11:MODECFG + phase: TCAV:DIAG0:11:PREQ + phase_avgnt: TCAV:DIAG0:11:PACT_AVGNT + phase_fbenb: TCAV:DIAG0:11:PFBENB + phase_fbst: TCAV:DIAG0:11:PFBST + rf_enable: TCAV:DIAG0:11:RF_ENABLE + control_name: TCAV:DIAG0:11 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + l_eff: 0.508 + rf_freq: 2856.0 + sum_l_meters: 53.313 + type: LCAV +wires: + WSDG01: + controls_information: + PVs: + abort_scan: WIRE:DIAG0:424:MOTR.STOP + install_angle: WIRE:DIAG0:424:INSTALLANGLE + motor: WIRE:DIAG0:424:MOTR + motor_rbv: WIRE:DIAG0:424:MOTR.RBV + scan_pulses: WIRE:DIAG0:424:SCANPULSES + speed: WIRE:DIAG0:424:MOTR.VELO + speed_max: WIRE:DIAG0:424:MOTR.VMAX + speed_min: WIRE:DIAG0:424:MOTR.VBAS + start_scan: WIRE:DIAG0:424:STARTSCAN + u_size: WIRE:DIAG0:424:UWIRESIZE + u_wire_inner: WIRE:DIAG0:424:UWIREINNER + u_wire_outer: WIRE:DIAG0:424:UWIREOUTER + use_u_wire: WIRE:DIAG0:424:USEUWIRE + use_x_wire: WIRE:DIAG0:424:USEXWIRE + use_y_wire: WIRE:DIAG0:424:USEYWIRE + x_size: WIRE:DIAG0:424:XWIRESIZE + x_wire_inner: WIRE:DIAG0:424:XWIREINNER + x_wire_outer: WIRE:DIAG0:424:XWIREOUTER + y_size: WIRE:DIAG0:424:YWIRESIZE + y_wire_inner: WIRE:DIAG0:424:YWIREINNER + y_wire_outer: WIRE:DIAG0:424:YWIREOUTER + control_name: WIRE:DIAG0:424 + metadata: + area: DIAG0 + beam_path: + - SC_DIAG0 + bpms_after_wire: + - BPMS:DIAG0:470 + - BPMS:DIAG0:520 + bpms_before_wire: + - BPMS:DIAG0:190 + - BPMS:DIAG0:210 + - BPMS:DIAG0:230 + - BPMS:DIAG0:270 + - BPMS:DIAG0:285 + - BPMS:DIAG0:330 + - BPMS:DIAG0:370 + - BPMS:DIAG0:390 + detectors: + - SBLM01A:DIAG0 + - LBLM01A:HTR + - LBLM01B:HTR + sum_l_meters: 57.113 + type: WIRE diff --git a/tests/test_data/example.db b/tests/test_data/example.db new file mode 100644 index 0000000..d16a0e5 Binary files /dev/null and b/tests/test_data/example.db differ diff --git a/tests/test_data/expected_areas.csv b/tests/test_data/expected_areas.csv new file mode 100644 index 0000000..ef0fd7f --- /dev/null +++ b/tests/test_data/expected_areas.csv @@ -0,0 +1 @@ +GUNB,L0B,LR20,HTR,COL0,DIAG0,COL,L1B,BC1B,COL1,L2B,BC2B,EMIT2,L3B,EXT,DOG,SFTDMP,INJ,L0F,DL10,L1F,BC11_1,BC11_2,L2F,BYP,BC14_1,BC14E,BC14P,BC14_2,L3F_1,SCAV,L3F_2,BC20,FF20,COMMON,EXPT20,SPECT2,GUN,GSPEC,L0,DL1,GTL,SPEC,L1,LI21,BC1,L2,BC2,LI24,L3,LI27,LI28,SPH,SPD,SPS,DASEL,LI30,CLTH,SLTD,SLTH,SLTS,CLTS,BSY,BSYS,BSYH,BSYA,LTU,LTUH,LTUS,UNDH,UND,UNDS,DMPH,DMPS,DMPH_1,DUMP,DMPHH,SFTS,SFTH,HXTES,SXTES,S10-PO,S10-CO,S10-EL,LI20,ELECTR,POSITR,52LINE,LI19,LI29,ASTA,LI18,EIC,LI25,HXR,SXR,SXX,LI15 diff --git a/tests/test_data/expected_beampaths.csv b/tests/test_data/expected_beampaths.csv new file mode 100644 index 0000000..fc4cd9a --- /dev/null +++ b/tests/test_data/expected_beampaths.csv @@ -0,0 +1 @@ +CU_ALINE,CU_GSPEC,CU_HTXI,CU_HXR,CU_HXTES,CU_SFTH,CU_SPEC,CU_SXR,F2_ELEC,F2_POSI,F2_SCAV,SC_BSYD,SC_DASEL,SC_DIAG0,SC_HXR,SC_S2_X,SC_SFTS,SC_STMO,SC_STXI,SC_SXR \ No newline at end of file diff --git a/tests/test_directory_service.py b/tests/test_directory_service.py new file mode 100644 index 0000000..3891a08 --- /dev/null +++ b/tests/test_directory_service.py @@ -0,0 +1,10 @@ +import unittest +import slac_db.directory_service + +class TestDirectoyrService(unittest.TestCase): + def test_get_otrdg02_pvs(self): + all_pvs = slac_db.directory_service.get_addresses( + device="OTRDG04", + ) + num_pvs = 2838 + self.assertEqual(len(all_pvs), num_pvs) diff --git a/tests/test_oracle.py b/tests/test_oracle.py new file mode 100644 index 0000000..0d1263f --- /dev/null +++ b/tests/test_oracle.py @@ -0,0 +1,53 @@ +import unittest +import csv +from pathlib import Path +import slac_db.oracle +import slac_db.directory_service + + +test_data_path = Path(__file__).parent / 'test_data' + +class TestExample(unittest.TestCase): + + def setUp(self): + p = test_data_path / 'example.db' + slac_db.oracle._init_db(str(p)) + + def test_get_example_areas(self): + expected_areas = ["AREA1", "AREA2", "AREA3"] + areas = slac_db.oracle.get_areas() + self.assertEqual(areas, expected_areas) + + def test_get_example_beampaths(self): + expected_areas = ["LINE1", "LINE2", "LINE3"] + areas = slac_db.oracle.get_beampaths() + self.assertEqual(areas, expected_areas) + +class TestOracle(unittest.TestCase): + + def setUp(self): + slac_db.oracle._init_db() + + def test_get_oracle_areas(self): + p = test_data_path / 'expected_areas.csv' + with open(str(p), 'r', newline='') as f: + reader = csv.reader(f) + expected_areas = next(reader) + areas = slac_db.oracle.get_areas() + self.assertEqual(areas, expected_areas) + + def test_get_oracle_beampaths(self): + p = test_data_path / 'expected_beampaths.csv' + with open(str(p), 'r', newline='') as f: + reader = csv.reader(f) + expected_beampaths = next(reader) + beampaths = slac_db.oracle.get_beampaths() + self.assertEqual(beampaths, expected_beampaths) + + def test_get_profile_monitors(self): + profile_monitors = slac_db.oracle.get_devices( + area="DIAG0", + device_type="PROF" + ) + expected_devices = ("OTRDG02", "OTRDG04") + self.assertEqual(profile_monitors, expected_devices)