Why is something the way it is
Aim |
Description |
---|---|
Reduce boilerplate |
At the moment you can write a simple asyn port driver in code, but there is a lot of boilerplate to connect it to the outside world. The createParam section, the database template records, and the lowest level screens are all quite repetitive and each layer looks like it could be autogenerated without much extra information |
Reduce copy paste errors |
At the moment it is easy to create screens and database templates via copy and paste, but not changing a record name or parameter leads to hard to track down errors |
Support site specific styles for screens |
Each site has their own style for screens, and many sites have their own site specific display manager. Rather than start with one display manager and convert, PVI takes a cut down Channel description (just a type, pv and widget), and lets the site specific template generate the screen according to local styles |
How it works
The YAML file contains information about each asyn parameter that will be exposed by the driver, it’s name, type, description, initial value, which record type it uses, whether it is writeable or read only, which widget should be used, etc. PVI reads these and passes them to Producer that creates intermediate Record, Channel and AsynParam objects. These are passed to a site specific Formatter which takes the tree of intermediate objects and writes a parameter CPP file, database template, and site specific screens to disk.
YAML file
The YAML file is formed of a number of sections:
Section |
Description |
---|---|
includes |
The YAML files to use as base classes for superclasses |
local |
A local override YAML file for site specific changes |
producer |
Producer that knows how to create Records and Channels from the Components |
formatter |
Site specific Formatter which can format the output files |
components |
Tree of Components for each logical asyn parameter arranged in logical GUI groups |
The Components are created from the YAML file with local overrides (also incorporating the base classes for screens). These are passed to the Producer which produces AsynParameters, Records and Channels. These are then passed to the Formatter which outputs them to file:
Here’s a cut down pilatus.yaml file that might describe a parameter in a detector:
# yaml-language-server: $schema=../../../schemas/pvi.producer.schema.json
type: AsynProducer
parent: ADDriver
label: Pilatus Detector
prefix: $(P)$(R)
asyn_port: $(PORT)
address: $(ADDR)
timeout: $(TIMEOUT)
parameters:
- type: Group
name: GroupOne
layout:
type: Grid
children:
- type: AsynBinary
name: ResetPower
description: ResetPower
drv_info: RESET_POWER
access: W
record_fields:
ONAM: Reset
ZNAM: Done
index_name: PilatusResetPower
- type: AsynBusy
name: ThresholdApply
description: ThresholdApply
drv_info: THRESHOLD_APPLY
access: W
record_fields:
ONAM: Apply
ZNAM: Done
index_name: PilatusThresholdApply
- type: AsynFloat64
name: ImageFileTmot
description: Timeout for image file
drv_info: IMAGE_FILE_TMOT
access: W
initial: 20.0
record_fields:
EGU: s
PREC: 3
index_name: PilatusImageFileTmot
- type: AsynFloat64
name: Wavelength
description: Wavelength
drv_info: WAVELENGTH
access: W
initial: 1.54
record_fields:
EGU: Angstroms
PREC: 4
index_name: PilatusWavelength
- type: AsynFloat64
name: EnergyLow
description: EnergyLow
drv_info: ENERGY_LOW
access: W
initial: 0.0
record_fields:
EGU: eV
PREC: 3
index_name: PilatusEnergyLow
- type: AsynFloat64
name: EnergyHigh
description: EnergyHigh
drv_info: ENERGY_HIGH
access: W
initial: 0.0
record_fields:
EGU: eV
PREC: 3
index_name: PilatusEnergyHigh
- type: AsynFloat64
name: DetDist
description: DetDist
drv_info: DET_DIST
access: W
initial: 1000.0
record_fields:
EGU: mm
PREC: 3
index_name: PilatusDetDist
- type: AsynFloat64
name: DetVOffset
description: DetVOffset
drv_info: DET_VOFFSET
access: W
initial: 0.0
record_fields:
EGU: mm
PREC: 3
index_name: PilatusDetVOffset
- type: AsynFloat64
name: BeamX
description: BeamX
drv_info: BEAM_X
access: W
initial: 0.0
record_fields:
EGU: pixels
PREC: 3
index_name: PilatusBeamX
- type: AsynFloat64
name: BeamY
description: BeamY
drv_info: BEAM_Y
access: W
initial: 0.0
record_fields:
EGU: pixels
PREC: 3
index_name: PilatusBeamY
- type: AsynFloat64
name: Flux
description: Flux
drv_info: FLUX
access: W
initial: 0.0
record_fields:
EGU: ph/s
PREC: 4
index_name: PilatusFlux
- type: AsynFloat64
name: FilterTransm
description: FilterTransm
drv_info: FILTER_TRANSM
access: W
initial: 1.0
record_fields:
PREC: 4
index_name: PilatusFilterTransm
- type: AsynFloat64
name: StartAngle
description: StartAngle
drv_info: START_ANGLE
access: W
initial: 0.0
record_fields:
EGU: deg
PREC: 4
index_name: PilatusStartAngle
- type: AsynFloat64
name: AngleIncr
description: AngleIncr
drv_info: ANGLE_INCR
access: W
initial: 0.1
record_fields:
EGU: deg
PREC: 4
index_name: PilatusAngleIncr
- type: AsynFloat64
name: Det2theta
description: Det2theta
drv_info: DET_2THETA
access: W
initial: 0.0
record_fields:
EGU: deg
PREC: 4
index_name: PilatusDet2theta
- type: AsynFloat64
name: Polarization
description: Polarization
drv_info: POLARIZATION
access: W
initial: 0.99
record_fields:
PREC: 4
index_name: PilatusPolarization
- type: AsynFloat64
name: Alpha
description: Alpha
drv_info: ALPHA
access: W
initial: 0.0
record_fields:
EGU: deg
PREC: 4
index_name: PilatusAlpha
- type: AsynFloat64
name: Kappa
description: Kappa
drv_info: KAPPA
access: W
initial: 0.0
record_fields:
EGU: deg
PREC: 4
index_name: PilatusKappa
- type: AsynFloat64
name: Phi
description: Phi
drv_info: PHI
access: W
initial: 0.0
record_fields:
EGU: deg
PREC: 4
index_name: PilatusPhi
- type: AsynFloat64
name: PhiIncr
description: PhiIncr
drv_info: PHI_INCR
access: W
initial: 0.1
record_fields:
EGU: deg
PREC: 4
index_name: PilatusPhiIncr
- type: AsynFloat64
name: Chi
description: Chi
drv_info: CHI
access: W
initial: 0.0
record_fields:
EGU: deg
PREC: 4
index_name: PilatusChi
- type: AsynFloat64
name: ChiIncr
description: ChiIncr
drv_info: CHI_INCR
access: W
initial: 0.1
record_fields:
EGU: deg
PREC: 4
index_name: PilatusChiIncr
- type: AsynFloat64
name: Omega
description: Omega
drv_info: OMEGA
access: W
initial: 0.0
record_fields:
EGU: deg
PREC: 4
index_name: PilatusOmega
- type: AsynFloat64
name: OmegaIncr
description: OmegaIncr
drv_info: OMEGA_INCR
access: W
initial: 0.1
record_fields:
EGU: deg
PREC: 4
index_name: PilatusOmegaIncr
- type: AsynString
name: OscillAxis
description: OscillAxis
drv_info: OSCILL_AXIS
access: W
initial: X, CW
index_name: PilatusOscillAxis
- type: AsynLong
name: NumOscill
description: NumOscill
drv_info: NUM_OSCILL
access: W
initial: 1
index_name: PilatusNumOscill
- type: AsynWaveform
name: BadPixelFile
description: BadPixelFile
drv_info: BAD_PIXEL_FILE
access: W
#initial: '0'
record_fields:
FTVL: CHAR
NELM: 256
index_name: PilatusBadPixelFile
- type: AsynWaveform
name: FlatFieldFile
description: FlatFieldFile
drv_info: FLAT_FIELD_FILE
access: W
# initial: '0'
record_fields:
FTVL: CHAR
NELM: 256
index_name: PilatusFlatFieldFile
- type: AsynWaveform
name: CbfTemplateFile
description: CbfTemplateFile
drv_info: CBFTEMPLATEFILE
access: W
# initial: '0'
record_fields:
FTVL: CHAR
NELM: 256
index_name: PilatusCbfTemplateFile
- type: AsynWaveform
name: HeaderString
description: HeaderString
drv_info: HEADERSTRING
access: W
# initial: '0'
record_fields:
FTVL: CHAR
NELM: 68
index_name: PilatusHeaderString
- type: AsynBinary
name: Armed
description: Armed
drv_info: ARMED
access: R
read_record_suffix: Armed
record_fields:
SCAN: I/O Intr
ONAM: Armed
ZNAM: Unarmed
index_name: PilatusArmed
- type: AsynLong
name: NumBadPixels
description: Number of bad pixels
drv_info: NUM_BAD_PIXELS
access: R
read_record_suffix: NumBadPixels
record_fields:
SCAN: I/O Intr
index_name: PilatusNumBadPixels
- type: AsynBinary
name: FlatFieldValid
description: Flat field valid
drv_info: FLAT_FIELD_VALID
access: R
read_record_suffix: FlatFieldValid
record_fields:
SCAN: I/O Intr
ONAM: Yes
ZNAM: No
index_name: PilatusFlatFieldValid
- type: AsynInt32
name: PixelCutOff
description: PixelCutOff_RBV
drv_info: PIXEL_CUTOFF
access: R
record_fields:
SCAN: I/O Intr
EGU: counts
index_name: PilatusPixelCutOff
- type: AsynFloat64
name: Temp0
description: Temp0_RBV
drv_info: TH_TEMP_0
access: R
record_fields:
SCAN: I/O Intr
EGU: C
PREC: 1
index_name: PilatusThTemp0
- type: AsynFloat64
name: Temp1
description: Temp1_RBV
drv_info: TH_TEMP_1
access: R
record_fields:
SCAN: I/O Intr
EGU: C
PREC: 1
index_name: PilatusThTemp1
- type: AsynFloat64
name: Temp2
description: Temp2_RBV
drv_info: TH_TEMP_2
access: R
record_fields:
SCAN: I/O Intr
EGU: C
PREC: 1
index_name: PilatusThTemp2
- type: AsynFloat64
name: Humid0
description: Humid0_RBV
drv_info: TH_HUMID_0
access: R
record_fields:
SCAN: I/O Intr
EGU: '%'
PREC: 1
index_name: PilatusThHumid0
- type: AsynFloat64
name: Humid1
description: Humid1_RBV
drv_info: TH_HUMID_1
access: R
record_fields:
SCAN: I/O Intr
EGU: '%'
PREC: 1
index_name: PilatusThHumid1
- type: AsynFloat64
name: Humid2
description: Humid2_RBV
drv_info: TH_HUMID_2
access: R
record_fields:
SCAN: I/O Intr
EGU: '%'
PREC: 1
index_name: PilatusThHumid2
- type: AsynString
name: TVXVersion
description: TVXVersion_RBV
drv_info: TVXVERSION
access: R
record_fields:
SCAN: I/O Intr
index_name: PilatusTvxVersion
- type: AsynLong
name: ResetPowerTime
description: Reset module power wait time
drv_info: RESET_POWER_TIME
initial: 1
record_fields:
SCAN: I/O Intr
EGU: Seconds
index_name: PilatusResetPowerTime
- type: AsynFloat64
name: DelayTime
description: DelayTime
drv_info: DELAY_TIME
initial: 0.0
record_fields:
SCAN: I/O Intr
EGU: s
PREC: 6
index_name: PilatusDelayTime
- type: AsynFloat64
name: ThresholdEnergy
description: Energy threshold
drv_info: THRESHOLD
initial: 10.0
record_fields:
SCAN: I/O Intr
EGU: keV
PREC: 3
index_name: PilatusThreshold
- type: AsynBinary
name: ThresholdAutoApply
description: ThresholdAutoApply
drv_info: THRESHOLD_AUTO_APPLY
initial: 1
record_fields:
SCAN: I/O Intr
ONAM: Yes
ZNAM: No
index_name: PilatusThresholdAutoApply
- type: AsynFloat64
name: Energy
description: X-ray Energy
drv_info: ENERGY
initial: 20.0
record_fields:
SCAN: I/O Intr
EGU: keV
PREC: 3
index_name: PilatusEnergy
- type: AsynLong
name: MinFlatField
description: Minimum flat field value
drv_info: MIN_FLAT_FIELD
initial: 100
record_fields:
SCAN: I/O Intr
EGU: Counts
index_name: PilatusMinFlatField
- type: AsynMultiBitBinary
name: GapFill
description: GapFill
drv_info: GAP_FILL
initial: 0
record_fields:
SCAN: I/O Intr
ONST: '0'
ONVL: 0
TWST: '-1'
TWVL: -1
ZRST: N.A.
ZRVL: 2
index_name: PilatusGapFill
Screen files
The intermediate objects are a number of Channel instances. These contain basic types (like Combo, TextInput, TextUpdate, LED, Group) and some creation hints (like label, grouping, description, display_form), but no X, Y, Width, Height or colour information. They may represent either a single widget or pair of demand/readback widgets.
The site-specific Formatter consumes these Channel objects, then produces a screen with style, sizing and layout that can be customized to the site. This means that the default layout (big screen with lots of widgets arranged in group boxes) could be produced for one site, then another site could make lots of little screens with one group per screen. Styling is also covered, so the blue/grey MEDM screens and green/grey EDM screens can be customized to fit the site style guide.
HTML Documentation
The Parameter and record sections of the existing documentation could be reproduced, in tabular form as a csv file that can be included in rst docs:
Parameter |
Records |
Description |
---|---|---|
GroupOne |
||
ResetPower |
bo: $(P)$(R)ResetPower |
ResetPower |
ThresholdApply |
busy: $(P)$(R)ThresholdApply |
ThresholdApply |
ImageFileTmot |
ao: $(P)$(R)ImageFileTmot |
Timeout for image file |
Wavelength |
ao: $(P)$(R)Wavelength |
Wavelength |
EnergyLow |
ao: $(P)$(R)EnergyLow |
EnergyLow |
EnergyHigh |
ao: $(P)$(R)EnergyHigh |
EnergyHigh |
DetDist |
ao: $(P)$(R)DetDist |
DetDist |
DetVOffset |
ao: $(P)$(R)DetVOffset |
DetVOffset |
BeamX |
ao: $(P)$(R)BeamX |
BeamX |
BeamY |
ao: $(P)$(R)BeamY |
BeamY |
Flux |
ao: $(P)$(R)Flux |
Flux |
FilterTransm |
ao: $(P)$(R)FilterTransm |
FilterTransm |
StartAngle |
ao: $(P)$(R)StartAngle |
StartAngle |
AngleIncr |
ao: $(P)$(R)AngleIncr |
AngleIncr |
Det2theta |
ao: $(P)$(R)Det2theta |
Det2theta |
Polarization |
ao: $(P)$(R)Polarization |
Polarization |
Alpha |
ao: $(P)$(R)Alpha |
Alpha |
Kappa |
ao: $(P)$(R)Kappa |
Kappa |
Phi |
ao: $(P)$(R)Phi |
Phi |
PhiIncr |
ao: $(P)$(R)PhiIncr |
PhiIncr |
Chi |
ao: $(P)$(R)Chi |
Chi |
ChiIncr |
ao: $(P)$(R)ChiIncr |
ChiIncr |
Omega |
ao: $(P)$(R)Omega |
Omega |
OmegaIncr |
ao: $(P)$(R)OmegaIncr |
OmegaIncr |
OscillAxis |
stringout: $(P)$(R)OscillAxis |
OscillAxis |
NumOscill |
longout: $(P)$(R)NumOscill |
NumOscill |
BadPixelFile |
waveform: $(P)$(R)BadPixelFile |
BadPixelFile |
FlatFieldFile |
waveform: $(P)$(R)FlatFieldFile |
FlatFieldFile |
CbfTemplateFile |
waveform: $(P)$(R)CbfTemplateFile |
CbfTemplateFile |
HeaderString |
waveform: $(P)$(R)HeaderString |
HeaderString |
Armed |
bi: $(P)$(R)Armed |
Armed |
NumBadPixels |
longin: $(P)$(R)NumBadPixels |
Number of bad pixels |
FlatFieldValid |
bi: $(P)$(R)FlatFieldValid |
Flat field valid |
PixelCutOff |
ai: $(P)$(R)PixelCutOff_RBV |
PixelCutOff_RBV |
Temp0 |
ai: $(P)$(R)Temp0_RBV |
Temp0_RBV |
Temp1 |
ai: $(P)$(R)Temp1_RBV |
Temp1_RBV |
Temp2 |
ai: $(P)$(R)Temp2_RBV |
Temp2_RBV |
Humid0 |
ai: $(P)$(R)Humid0_RBV |
Humid0_RBV |
Humid1 |
ai: $(P)$(R)Humid1_RBV |
Humid1_RBV |
Humid2 |
ai: $(P)$(R)Humid2_RBV |
Humid2_RBV |
TVXVersion |
stringin: $(P)$(R)TVXVersion_RBV |
TVXVersion_RBV |
ResetPowerTime |
longin: $(P)$(R)ResetPowerTime_RBV longout: $(P)$(R)ResetPowerTime |
Reset module power wait time |
DelayTime |
ai: $(P)$(R)DelayTime_RBV ao: $(P)$(R)DelayTime |
DelayTime |
ThresholdEnergy |
ai: $(P)$(R)ThresholdEnergy_RBV ao: $(P)$(R)ThresholdEnergy |
Energy threshold |
ThresholdAutoApply |
bi: $(P)$(R)ThresholdAutoApply_RBV bo: $(P)$(R)ThresholdAutoApply |
ThresholdAutoApply |
Energy |
ai: $(P)$(R)Energy_RBV ao: $(P)$(R)Energy |
X-ray Energy |
MinFlatField |
longin: $(P)$(R)MinFlatField_RBV longout: $(P)$(R)MinFlatField |
Minimum flat field value |
GapFill |
mbbi: $(P)$(R)GapFill_RBV mbbo: $(P)$(R)GapFill |
GapFill |
Questions
I am fairly happy with the scheme set out above, but there are a lot of implementation questions. Here are the most pressing:
One-time generation and checked into source control or generated by Makefile?
The process would probably be:
If pvi cli tool available, build products as part of make
Check in products to source control
End users will only regenerate build products if pvi tool installed
ADGenICam would be supported by building a GenICamProducer which took no components, just a path to a GenICam XML file
Which screen tools to support?
I suggest creating adl and edl files initially, following the example of makeAdl.py in ADGenICam, then expanding to support opi, bob and ui files natively. This would avoid needing screen converters installed