Why is something the way it is

digraph pvi_flowchart { bgcolor=transparent rankdir=LR node [fontname=Arial fontsize=10 shape=box style=filled fillcolor="#8BC4E9"] edge [fontname=Arial fontsize=10 arrowhead=vee] { rank=same; "pilatus.pvi.yaml" "pilatus.cpp" "pilatus.h" } PVI [shape=doublecircle] "pilatus.local.yaml" -> PVI "pilatus.pvi.yaml" -> PVI PVI -> "pilatus_parameters.h" PVI -> "pilatus_parameters.template" PVI -> "pilatus_parameters.opi" PVI -> "pilatus_parameters.adl" PVI -> "pilatus_parameters.edl" PVI -> "pilatus_parameters.csv" "pilatus_parameters.template" -> "pilatus.template" [label="included in"] "pilatus_parameters.h" -> "pilatus.h" [label="included in"] "pilatus.cpp" -> "libPilatus.so" "pilatus.h" -> "libPilatus.so" "pilatus_parameters.csv" -> "pilatus.rst" [label="included in"] "pilatus_parameters.adl" -> "pilatus.adl" [label="linked from"] "pilatus_parameters.edl" -> "pilatus.edl" [label="linked from"] "pilatus_parameters.opi" -> "pilatus.opi" [label="linked from"] }

Aims of PVI

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:

digraph pvi_products { bgcolor=transparent node [fontname=Arial fontsize=10 shape=box style=filled fillcolor="#8BC4E9"] edge [fontname=Arial fontsize=10 arrowhead=vee] Intermediate [label="[Record(),\n Channel(),\n AsynParameter()]"] Products [label="Template\nScreens\nDriver Params\nDocumentation"] {rank=same; Components -> Producer -> Intermediate -> Formatter -> Products} }

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:

Pilatus Parameters

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