diff --git a/slac_db/create/combined.py b/slac_db/create/combined.py index 691f73c..1564edd 100644 --- a/slac_db/create/combined.py +++ b/slac_db/create/combined.py @@ -79,10 +79,14 @@ def _build(): for r in slac_db.oracle.get_all_rows(): if r["element"] not in self.device_names: continue + cs_name = r["control system name"] or "" + # Klystron stations share keyword=LCAV with TCAVs but need + # their own accessor block keyed as "KLYS". + d_type = "KLYS" if "KLYS" in cs_name else r["keyword"] yield from _meta( r["element"], - r["control system name"], - r["keyword"], + cs_name, + d_type, ) def _get_accessors(d_type, address): @@ -99,6 +103,7 @@ def _get_accessors(d_type, address): yield (address, accessor) def _meta(device, pv_head, d_type): + override = self.accessor_overrides.get(device, {}) for pv_tail in self.address_map.get(pv_head, [None]): if pv_tail is None: continue @@ -110,9 +115,18 @@ def _meta(device, pv_head, d_type): accessor_name=accessor, ) for address, accessor in accessor_names + if accessor not in override ] + for accessor_name, cs_address in override.items(): + if cs_address: + yield PKDict( + device_name=device, + cs_address=cs_address, + accessor_name=accessor_name, + ) self.accessor_map = slac_db.io.read_dict(_ACCESSOR_YAML) + self.accessor_overrides = self.accessor_map.pop("_overrides", {}) self.accessor_meta = list(_build()) def _address_map(self): diff --git a/slac_db/create/lcls_elements.py b/slac_db/create/lcls_elements.py index 26ad239..34db9fc 100644 --- a/slac_db/create/lcls_elements.py +++ b/slac_db/create/lcls_elements.py @@ -46,7 +46,38 @@ def __init__(self, csv_source=None): def _parse_csv(self, reader): names = [r.lower() for r in next(reader)] i = 0 + # Track station names already recorded for KLYS sub-cavity dedup. + # Maps station_name -> index in self.rows for the canonical row. + # Keyed on the stripped element name (e.g. "K21_5") so that dirty + # cs_name data (truncated or transposed digits in sectors 12-19) + # cannot produce duplicate station entries. + _klys_seen = {} for row in reader: values = [None if v == '' else v for v in row] - self.rows[i] = dict(zip(names, values)) + d = dict(zip(names, values)) + element = d.get("element") or "" + cs_name = d.get("control system name") or "" + keyword = d.get("keyword") or "" + # Deduplicate klystron sub-cavities (K21_5A/B/C/D -> K21_5). + # A sub-cavity row is an LCAV whose element ends in A-D and whose + # cs_name contains "KLYS" (handles both "KLYS:LI{s}:{n}1" and + # legacy reversed form "LI{s}:KLYS:{n}1" used in sectors 12-19). + # Dedup key is the station name (element minus trailing letter) so + # that cs_name inconsistencies in the source data don't create + # duplicate entries (e.g. K12_3A has cs LI12:KLYS:3 while + # K12_3B/C/D have LI12:KLYS:31). + if ( + keyword == "LCAV" + and "KLYS" in cs_name + and len(element) > 1 + and element[-1] in "ABCD" + ): + station = element[:-1] # e.g. "K21_5A" -> "K21_5" + if station in _klys_seen: + # Already have a row for this station; skip this sub-cavity. + continue + # First time seeing this station: record it and rename element. + d["element"] = station + _klys_seen[station] = i + self.rows[i] = d i += 1 diff --git a/slac_db/package_data/accessor_names.yaml b/slac_db/package_data/accessor_names.yaml index 7397673..3e29113 100644 --- a/slac_db/package_data/accessor_names.yaml +++ b/slac_db/package_data/accessor_names.yaml @@ -93,3 +93,94 @@ IMON: INST: QDCRAW: qdcraw + +KLYS: + ENLD: amplitude + PHAS: phase + PACT: phase_actual + PDES: phase_desired + BEAMCODE1_STAT: beamcode1 + BEAMCODE2_STAT: beamcode2 + SWRD: swrd + STAT: stat + HDSC: hdsc + DSTA: dsta + +# Per-device PV overrides for stations that deviate from the normal pattern. +# Keys are device MAD names; values are flat dicts of accessor_name: full_pv_address. +# An empty string means the PV is absent and the accessor is omitted from the DB. +_overrides: + # --- GUN klystron --- + K20_6: + amplitude: GUN:IN20:1:GN1_AAVG + amplitude_des: GUN:IN20:1:GN1_ADES + phase_actual: GUN:IN20:1:GN1_PAVG + phase_desired: GUN:IN20:1:GN1_PDES + + # --- L0A injector linac --- + K20_7: + amplitude_beamcode1: ACCL:IN20:300:L0A_AACT_DS0 + amplitude_des_beamcode1: ACCL:IN20:300:L0A_ADES_DS0 + phase_actual_beamcode1: ACCL:IN20:300:L0A_PACT_DS0 + phase_desired_beamcode1: ACCL:IN20:300:L0A_PDES_DS0 + amplitude_beamcode2: ACCL:IN20:300:L0A_AACT_DS1 + amplitude_des_beamcode2: ACCL:IN20:300:L0A_ADES_DS1 + phase_actual_beamcode2: ACCL:IN20:300:L0A_PACT_DS1 + phase_desired_beamcode2: ACCL:IN20:300:L0A_PDES_DS1 + + # --- L0B injector linac --- + K20_8: + amplitude_beamcode1: ACCL:IN20:400:L0B_AACT_DS0 + amplitude_des_beamcode1: ACCL:IN20:400:L0B_ADES_DS0 + phase_actual_beamcode1: ACCL:IN20:400:L0B_PACT_DS0 + phase_desired_beamcode1: ACCL:IN20:400:L0B_PDES_DS0 + amplitude_beamcode2: ACCL:IN20:400:L0B_AACT_DS1 + amplitude_des_beamcode2: ACCL:IN20:400:L0B_ADES_DS1 + phase_actual_beamcode2: ACCL:IN20:400:L0B_PACT_DS1 + phase_desired_beamcode2: ACCL:IN20:400:L0B_PDES_DS1 + + # --- L1S sub-booster --- + K21_1: + amplitude_beamcode1: ACCL:LI21:1:L1S_AACT_DS0 + amplitude_des_beamcode1: ACCL:LI21:1:L1S_ADES_DS0 + phase_actual_beamcode1: ACCL:LI21:1:L1S_PACT_DS0 + phase_desired_beamcode1: ACCL:LI21:1:L1S_PDES_DS0 + amplitude_beamcode2: ACCL:LI21:1:L1S_AACT_DS1 + amplitude_des_beamcode2: ACCL:LI21:1:L1S_ADES_DS1 + phase_actual_beamcode2: ACCL:LI21:1:L1S_PACT_DS1 + phase_desired_beamcode2: ACCL:LI21:1:L1S_PDES_DS1 + + # --- L1X 3rd-harmonic linearizer --- + K21_2: + amplitude_beamcode1: ACCL:LI21:180:L1X_AACT_DS0 + amplitude_des_beamcode1: ACCL:LI21:180:L1X_ADES_DS0 + phase_actual_beamcode1: ACCL:LI21:180:L1X_PACT_DS0 + phase_desired_beamcode1: ACCL:LI21:180:L1X_PDES_DS0 + amplitude_beamcode2: ACCL:LI21:180:L1X_AACT_DS1 + amplitude_des_beamcode2: ACCL:LI21:180:L1X_ADES_DS1 + phase_actual_beamcode2: ACCL:LI21:180:L1X_PACT_DS1 + phase_desired_beamcode2: ACCL:LI21:180:L1X_PDES_DS1 + + # --- L2 feedback klystrons (normal ENLD/fault PVs, special phase PV) --- + K24_1: + phase_actual_beamcode1: ACCL:LI24:100:KLY_PDES:SETDATA_1 + phase_desired_beamcode1: ACCL:LI24:100:KLY_PDES:SETDATA_1 + phase_actual_beamcode2: ACCL:LI24:100:KLY_PDES:SETDATA_2 + phase_desired_beamcode2: ACCL:LI24:100:KLY_PDES:SETDATA_2 + + K24_2: + phase_actual_beamcode1: ACCL:LI24:200:KLY_PDES:SETDATA_1 + phase_desired_beamcode1: ACCL:LI24:200:KLY_PDES:SETDATA_1 + phase_actual_beamcode2: ACCL:LI24:200:KLY_PDES:SETDATA_2 + phase_desired_beamcode2: ACCL:LI24:200:KLY_PDES:SETDATA_2 + + K24_3: + phase_actual_beamcode1: ACCL:LI24:300:KLY_PDES:SETDATA_1 + phase_desired_beamcode1: ACCL:LI24:300:KLY_PDES:SETDATA_1 + phase_actual_beamcode2: ACCL:LI24:300:KLY_PDES:SETDATA_2 + phase_desired_beamcode2: ACCL:LI24:300:KLY_PDES:SETDATA_2 + + # --- Test Stand Station --- + K26_3: + is_ignored: KLYS:LI26:31:IGNORE.RVAL + accelerate: KLYS:LI26:31:TMODE_DES.RVAL diff --git a/slac_db/package_data/device.sqlite3 b/slac_db/package_data/device.sqlite3 index 8bfda6e..47c64d0 100644 --- a/slac_db/package_data/device.sqlite3 +++ b/slac_db/package_data/device.sqlite3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f41a7e4468c81061aab1ab73a5f4ad92bedf31d12fa08d1750dc97bd86456832 -size 331829248 +oid sha256:ec4c76025fd0ff2decda4ee4a0fff0bf4f889bbcf08738d9e6d89b2611cf52e8 +size 207077376 diff --git a/slac_db/package_data/lcls_elements.sqlite3 b/slac_db/package_data/lcls_elements.sqlite3 index 676b443..9cde246 100644 --- a/slac_db/package_data/lcls_elements.sqlite3 +++ b/slac_db/package_data/lcls_elements.sqlite3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f17ec5e98de1dda3b2e87ac238003440f26560d25d91f0e0633036c34db65b78 -size 765952 +oid sha256:2a065a646c6f6424e2f157408ed9c8cd590657627d174b59bd09a3dff981a009 +size 720896