Configuration Files

Motivation

Haven’s goal is to provide support for all of the spectroscopy beamlines. However, each beamline is different, and these differences are managed by a set of configuration files, similar to the .ini files used in the old LabView solution. To keep the complexity of these configuration files manageable, Haven gets much of the needed information from the IOCs directly.

Haven/Firefly should always load without a specific configuration file, but will probably not do anything useful.

Checking Configuration

If Haven is installed with pip, the command haven_config can be used to read configuration variables as they will be seen by Haven:

$ haven_config beamline
{'is_connected': False, 'name': 'SPC Beamline (sector unknown)'}
$ haven_config beamline.is_connected
False

Configuration File Priority

There are several sources of configuration files, described in detail below. They are loaded in the following order, with lower numbers taking precedence over higher numbers.

  1. Files listed in the $HAVEN_CONFIG_FILES

  2. ~/bluesky/instrument/iconfig.toml (for backwards compatibility)

  3. ~/bluesky/iconfig.toml (best place)

  4. iconfig_default.toml packaged with Haven

Unless there’s a good reason to do otherwise, most beamline configuration belongs in ~/bluesky/iconfig.toml.

For example, to enable support for our Universal Robotics robot Austin to 25-ID-C, open the file ~/bluesky/iconfig.toml and add the following:

[robot.Austin]
prefix = "25idAustin"

Note

The prevent accidental changes, the bluesky configuration files may not be writable by the user accounts at the beamline. For example, at 25-ID, the user account does not have permission to write to ~/bluesky/iconfig.toml so changes must be made as the staff account.

HAVEN_CONFIG_FILES Environment

If the environmental variable HAVEN_CONFIG_FILES is set to a comma-separated list of file path, then these files will take priority, with later entries superseding earlier entries.

~/bluesky/iconfig.toml

The file ~/bluesky/iconfig.toml will be read if it is present. This is the best place to put beamline-specific configuration.

The file ~/bluesky/instrument/iconfig.toml is also read for backwards compatibility. It should not be used for new deployments, and support for it may be removed without warning.

iconfig_default.toml

Haven includes an set of default configuration values in src/haven/iconfig_default.toml. This is mainly so that Haven and Firefly can still run during development without a dedicated configuration file. It also serves as a starting point for deploying Haven to a new beamline. See the section on testing below for suggestions on how to add default configuration.

Development and Testing

While adding features and tests to Haven, it is often necessary to read a configuration file, for example when testing functions that load devices through load_instrument(). However, the configuration that is loaded should not come from a real beamline configuration or else there is a risk of controlling real hardware while running tests.

To avoid this problem, pytest modifies the configuration file loading when running tests with pytest:

  1. Ignore any config files besides iconfig_default.toml.

  2. Add iconfig_testing.toml to the configuration

Additionally, all load_motors() style functions should accept an optional config argument, that will determine the configuration instead of using the above-mentioned priority.

If a feature is added to Haven that would benefit from beamline-specific configuration, it can be added in one of two places.

src/haven/iconfig_default.toml

This is the best choice if the device or feature is critical to the operation of Haven and/or Firefly, such as the beamline scheduling system. The values listed should still not point at real hardware, but should be sensible defaults or dummy values to allow Haven to function.

src/haven/iconfig_testing.toml

This is the best choice if the device or hardware is optional, and may or may not be present at any given beamline, for example, fluorescence detectors. This configuration should not point to real hardware.

Example Configuration

Below are some examples of configuration that can be re-used for new devices support or beamline setup.

iconfig_default.toml
# Defaults go here, then get updated by toml loader
[beamline]

name = "SPC Beamline (sector unknown)"
is_connected = false

[facility]

name = "Advanced Photon Source"
xray_source = "insertion device"

[database.databroker]

catalog = "bluesky"

# Cameras
# =======

[camera]

imagej_command = "imagej"
iconfig_testing.toml
[bss]

prefix = "255idc:bss"
beamline = "255-ID-C"

[xray_source]

type = "undulator"
prefix = "ID255:"

[queueserver]
kafka_topic = "s255idc_queueserver"
control_host = "localhost"
control_port = "60615"
info_host = "localhost"
info_port = "60625"

[database.tiled]

uri = "http://localhost:8337/"
entry_node = "255id_testing"

[shutter]
prefix = "PSS:99ID"

[shutter.front_end_shutter]

hutch = "A"
# open_pv = "PSS:99ID:FES_OPEN_EPICS.VAL"
# close_pv = "PSS:99ID:FES_CLOSE_EPICS.VAL"
# status_pv = "PSS:99ID:A_BEAM_PRESENT"

[shutter.hutch_shutter]

hutch = "C"
# open_pv = "PSS:99ID:SCS_OPEN_EPICS.VAL"
# close_pv = "PSS:99ID:SCS_CLOSE_EPICS.VAL"
# status_pv = "PSS:25ID:C_BEAM_PRESENT"

[undulator]

ioc = "id_ioc"

[monochromator]

ioc = "mono_ioc"
ioc_branch = "UP"  # For caQtDM macros


[ion_chamber]

[ion_chamber.scaler]
prefix = "scaler_ioc"
channels = [2]

[ion_chamber.preamp]
prefix = "preamp_ioc"

[ion_chamber.voltmeter]
prefix = "255idc:LabjackT7_"  # Don't include the labjack number

# Motors
# ======
# 
# Add a new section for each IOC (or IOC prefix) that has motors
# matching the format {prefix}:m1. The heading of the subsection
# ("VME_crate" in the example below) is a human-readable name that
# will become a label on the Ophyd Device. *num_motors* determines how
# many motors will be read. The example below will load three motors
# with PVs: "vme_crate_ioc:m1", "vme_crate_ioc:m2", and
# "vme_crate_ioc:m3".

[motor.VME_crate]
prefix = "255idVME"
num_motors = 3

# Keys for camera definitions must begin with "cam" (e.g. "camA", "camB")
[camera.camA]

name = "s25id-gige-A"
description = "GigE Vision A"
prefix = "255idgigeA"

[aerotech_stage.aerotech]

prefix = "255idc"
delay_prefix = "255idc:DG645"
pv_vert = ":m1"
pv_horiz = ":m2"

[power_supply.NHQ01]

prefix = "ps_ioc:NHQ01"
n_channels = 2

[slits.KB_slits]

prefix = "vme_crate_ioc:KB"
device_class = "BladeSlits"

[slits.whitebeam_slits]
# A single rotating aperture slit, like the 25-ID white/pinkbeam slits
prefix = "255ida:slits:US:"
device_class = "ApertureSlits"
pitch_motor = "m33"
yaw_motor = "m34"
horizontal_motor = "m35"
diagonal_motor = "m36"

# A bendable mirror, like the long KB at 25-ID-C
[kb_mirrors.LongKB_Cdn]
prefix = "255idcVME:LongKB_Cdn:"
horiz_upstream_motor = "m33"
horiz_downstream_motor = "m34"
horiz_upstream_bender = "m21"
horiz_downstream_bender = "m22"
vert_upstream_motor = "m46"
vert_downstream_motor = "m47"
vert_upstream_bender = "m56"
vert_downstream_bender = "m57"

# A non-bendable mirror, like the KB at 25-ID-C
[kb_mirrors.KB]
prefix = "255idcVME:KB:"
horiz_upstream_motor = "m35"
horiz_downstream_motor = "m36"
vert_upstream_motor = "m48"
vert_downstream_motor = "m49"

# A single bounch, high heat load mirror
[mirrors.ORM1]
prefix = "25ida:ORM1:"
device_class = "HighHeatLoadMirror"
bendable = false

[mirrors.ORM2]

prefix = "25ida:ORM2:"
device_class = "HighHeatLoadMirror"
bendable = true

# An optical table with two vertical motors
[table.downstream_table]
prefix = "255idcVME:"
transforms = "table_ds_trans:"
pseudo_motors = "table_ds:"
upstream_motor = "m21"
downstream_motor = "m22"

# An optical table with one vertical motor and one horizontal motor
[table.upstream_table]
prefix = "255idcVME:"
vertical_motor = "m26"
horizontal_motor = "m25"

[area_detector.sim_det]

prefix = "255idSimDet"
device_class = "SimDetector"

[lerix.lerix.rowland]

x_motor_pv = "255idVME:m1"
y_motor_pv = "255idVME:m2"
z_motor_pv = "255idVME:m3"
z1_motor_pv = "255idVME:m4"

[heater.capillary_heater]

prefix = "255idptc10"
device_class = "CapillaryHeater"

[robot.A]
prefix = "255idAustin"

# Managed IOC control PVs
[iocs]
255idb = "glados:ioc255idb"
255idc = "glados:ioc255idc"

[fluorescence_detector]

[dxp.vortex_me4]

prefix = "vortex_me4"
num_elements = 4

[dxp.canberra_Ge7]

prefix = "20xmap8"
num_elements = 4

[xspress.vortex_me4_xsp]

prefix = "vortex_me4_xsp"
num_elements = 4