diff --git a/example/t01-services/bl01t-di-cam-01/ADAravis.pvi.bob b/example/t01-services/bl01t-di-cam-01/ADAravis.pvi.bob
new file mode 100644
index 00000000..f8c1511b
--- /dev/null
+++ b/example/t01-services/bl01t-di-cam-01/ADAravis.pvi.bob
@@ -0,0 +1,2257 @@
+
+ ADAravis Camera
+ 0
+ 0
+ 1609
+ 899
+ 4
+ 4
+
+ Title
+ TITLE
+ ADAravis Camera
+ 0
+ 0
+ 1609
+ 25
+
+
+
+
+
+
+
+
+ true
+ 1
+
+
+ AD Aravis
+ 5
+ 30
+ 396
+ 331
+ true
+
+ Label
+ AR Frames Completed
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ARFramesCompleted
+ 155
+ 0
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ AR Frame Failures
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ARFrameFailures
+ 155
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ AR Frame Underruns
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ARFrameUnderruns
+ 155
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ AR Missing Packets
+ 0
+ 75
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ARMissingPackets
+ 155
+ 75
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ AR Resent Packets
+ 0
+ 100
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ARResentPackets
+ 155
+ 100
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ AR Packet Resend Enable
+ 0
+ 125
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)ARPacketResendEnable
+ 155
+ 125
+ 205
+ 20
+
+
+ Label
+ AR Packet Timeout
+ 0
+ 150
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)ARPacketTimeout
+ 155
+ 150
+ 205
+ 20
+ 1
+
+
+ Label
+ AR Frame Retention
+ 0
+ 175
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)ARFrameRetention
+ 155
+ 175
+ 205
+ 20
+ 1
+
+
+ Label
+ AR Reset Camera
+ 0
+ 200
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)ARResetCamera
+ 155
+ 200
+ 205
+ 20
+ 1
+
+
+ Label
+ AR Convert Pixel Format
+ 0
+ 225
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)ARConvertPixelFormat
+ 155
+ 225
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)ARConvertPixelFormat_RBV
+ 260
+ 225
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ AR Shift Dir
+ 0
+ 250
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)ARShiftDir
+ 155
+ 250
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)ARShiftDir_RBV
+ 260
+ 250
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ AR Shift Bits
+ 0
+ 275
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)ARShiftBits
+ 155
+ 275
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)ARShiftBits_RBV
+ 260
+ 275
+ 100
+ 20
+
+
+
+
+ 1
+
+
+
+ arvFeature Ungrouped
+ 5
+ 366
+ 396
+ 56
+ true
+
+ Label
+ AR Connect Camera
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)ARConnectCamera
+ 155
+ 0
+ 205
+ 20
+ 1
+
+
+
+ AD Collect
+ 5
+ 427
+ 396
+ 406
+ true
+
+ Label
+ Acquire Time
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)AcquireTime
+ 155
+ 0
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)AcquireTime_RBV
+ 260
+ 0
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Acquire Period
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)AcquirePeriod
+ 155
+ 25
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)AcquirePeriod_RBV
+ 260
+ 25
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Num Images Counter
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)NumImagesCounter_RBV
+ 155
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Num Images
+ 0
+ 75
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)NumImages
+ 155
+ 75
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)NumImages_RBV
+ 260
+ 75
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Num Exposures
+ 0
+ 100
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)NumExposures
+ 155
+ 100
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)NumExposures_RBV
+ 260
+ 100
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Image Mode
+ 0
+ 125
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)ImageMode
+ 155
+ 125
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)ImageMode_RBV
+ 260
+ 125
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Trigger Mode
+ 0
+ 150
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)TriggerMode
+ 155
+ 150
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)TriggerMode_RBV
+ 260
+ 150
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Detector State
+ 0
+ 175
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)DetectorState_RBV
+ 155
+ 175
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Status Message
+ 0
+ 200
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)StatusMessage_RBV
+ 155
+ 200
+ 205
+ 20
+
+
+
+
+ 1
+ 6
+
+
+ Label
+ Time Remaining
+ 0
+ 225
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)TimeRemaining_RBV
+ 155
+ 225
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Num Queued Arrays
+ 0
+ 250
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)NumQueuedArrays
+ 155
+ 250
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Wait For Plugins
+ 0
+ 275
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)WaitForPlugins
+
+ 247
+ 275
+ 20
+ 20
+
+
+ Label
+ Acquire
+ 0
+ 300
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)Acquire
+
+ 195
+ 300
+ 20
+ 20
+
+
+ LED
+ $(P)$(R)Acquire_RBV
+ 300
+ 300
+ 20
+ 20
+
+
+ Label
+ Array Counter
+ 0
+ 325
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)ArrayCounter
+ 155
+ 325
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)ArrayCounter_RBV
+ 260
+ 325
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Array Callbacks
+ 0
+ 350
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)ArrayCallbacks
+
+ 195
+ 350
+ 20
+ 20
+
+
+ LED
+ $(P)$(R)ArrayCallbacks_RBV
+ 300
+ 350
+ 20
+ 20
+
+
+
+ AD Epics Shutter
+ 5
+ 838
+ 396
+ 56
+ true
+
+ Label
+ Shutter Status
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ LED
+ $(P)$(R)ShutterStatus_RBV
+ 247
+ 0
+ 20
+ 20
+
+
+
+ AD Readout
+ 406
+ 30
+ 396
+ 431
+ true
+
+ Label
+ Max Size X
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)MaxSizeX_RBV
+ 155
+ 0
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Max Size Y
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)MaxSizeY_RBV
+ 155
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Bin X
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)BinX
+ 155
+ 50
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)BinX_RBV
+ 260
+ 50
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Bin Y
+ 0
+ 75
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)BinY
+ 155
+ 75
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)BinY_RBV
+ 260
+ 75
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Min Y
+ 0
+ 100
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)MinY
+ 155
+ 100
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)MinY_RBV
+ 260
+ 100
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Min X
+ 0
+ 125
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)MinX
+ 155
+ 125
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)MinX_RBV
+ 260
+ 125
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Reverse X
+ 0
+ 150
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)ReverseX
+
+ 195
+ 150
+ 20
+ 20
+
+
+ LED
+ $(P)$(R)ReverseX_RBV
+ 300
+ 150
+ 20
+ 20
+
+
+ Label
+ Reverse Y
+ 0
+ 175
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)ReverseY
+
+ 195
+ 175
+ 20
+ 20
+
+
+ LED
+ $(P)$(R)ReverseY_RBV
+ 300
+ 175
+ 20
+ 20
+
+
+ Label
+ Size X
+ 0
+ 200
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)SizeX
+ 155
+ 200
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)SizeX_RBV
+ 260
+ 200
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Size Y
+ 0
+ 225
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)SizeY
+ 155
+ 225
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)SizeY_RBV
+ 260
+ 225
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Gain
+ 0
+ 250
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)Gain
+ 155
+ 250
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)Gain_RBV
+ 260
+ 250
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Array Size X
+ 0
+ 275
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ArraySizeX_RBV
+ 155
+ 275
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Array Size Y
+ 0
+ 300
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ArraySizeY_RBV
+ 155
+ 300
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Array Size
+ 0
+ 325
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ArraySize_RBV
+ 155
+ 325
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Data Type
+ 0
+ 350
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)DataType
+ 155
+ 350
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)DataType_RBV
+ 260
+ 350
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Color Mode
+ 0
+ 375
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)ColorMode
+ 155
+ 375
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)ColorMode_RBV
+ 260
+ 375
+ 100
+ 20
+
+
+
+
+ 1
+
+
+
+ AD Shutter
+ 406
+ 466
+ 396
+ 131
+ true
+
+ Label
+ Shutter Mode
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)ShutterMode
+ 155
+ 0
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)ShutterMode_RBV
+ 260
+ 0
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Shutter Control
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)ShutterControl
+
+ 195
+ 25
+ 20
+ 20
+
+
+ LED
+ $(P)$(R)ShutterControl_RBV
+ 300
+ 25
+ 20
+ 20
+
+
+ Label
+ Shutter Open Delay
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)ShutterOpenDelay
+ 155
+ 50
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)ShutterOpenDelay_RBV
+ 260
+ 50
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Shutter Close Delay
+ 0
+ 75
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)ShutterCloseDelay
+ 155
+ 75
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)ShutterCloseDelay_RBV
+ 260
+ 75
+ 100
+ 20
+
+
+
+
+ 1
+
+
+
+ ADDriver Ungrouped
+ 406
+ 602
+ 396
+ 231
+ true
+
+ Label
+ Read Status
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)ReadStatus
+
+ 247
+ 0
+ 20
+ 20
+
+
+ Label
+ Num Exposures Counter
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)NumExposuresCounter_RBV
+ 155
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ String To Server
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)StringToServer_RBV
+ 155
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+ 6
+
+
+ Label
+ String From Server
+ 0
+ 75
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)StringFromServer_RBV
+ 155
+ 75
+ 205
+ 20
+
+
+
+
+ 1
+ 6
+
+
+ Label
+ Shutter Control EPICS
+ 0
+ 100
+ 150
+ 20
+ No description provided
+
+
+ LED
+ $(P)$(R)ShutterControlEPICS
+ 247
+ 100
+ 20
+ 20
+
+
+ Label
+ Temperature Actual
+ 0
+ 125
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)TemperatureActual
+ 155
+ 125
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Frame Type
+ 0
+ 150
+ 150
+ 20
+ No description provided
+
+
+ ComboBox
+ $(P)$(R)FrameType
+ 155
+ 150
+ 100
+ 20
+
+
+ TextUpdate
+ $(P)$(R)FrameType_RBV
+ 260
+ 150
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Temperature
+ 0
+ 175
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)Temperature
+ 155
+ 175
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)Temperature_RBV
+ 260
+ 175
+ 100
+ 20
+
+
+
+
+ 1
+
+
+
+ AD Attr File
+ 807
+ 30
+ 396
+ 106
+ true
+
+ Label
+ ND Attributes File
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)NDAttributesFile
+ 155
+ 0
+ 205
+ 20
+ 1
+ 6
+
+
+ Label
+ ND Attributes Macros
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)NDAttributesMacros
+ 155
+ 25
+ 205
+ 20
+ 1
+ 6
+
+
+ Label
+ ND Attributes Status
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)NDAttributesStatus
+ 155
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
+ AD Buffers
+ 807
+ 141
+ 396
+ 231
+ true
+
+ Label
+ Pre Alloc Buffers
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)PreAllocBuffers
+
+ 247
+ 0
+ 20
+ 20
+
+
+ Label
+ Num Pre Alloc Buffers
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)NumPreAllocBuffers
+ 155
+ 25
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)NumPreAllocBuffers_RBV
+ 260
+ 25
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Pool Alloc Buffers
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)PoolAllocBuffers
+ 155
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Pool Free Buffers
+ 0
+ 75
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)PoolFreeBuffers
+ 155
+ 75
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Pool Max Mem
+ 0
+ 100
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)PoolMaxMem
+ 155
+ 100
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Pool Used Mem
+ 0
+ 125
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)PoolUsedMem
+ 155
+ 125
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Pool Poll Stats
+ 0
+ 150
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)PoolPollStats
+
+ 247
+ 150
+ 20
+ 20
+
+
+ Label
+ Empty Free List
+ 0
+ 175
+ 150
+ 20
+ No description provided
+
+
+ ToggleButton
+ $(P)$(R)EmptyFreeList
+
+ 247
+ 175
+ 20
+ 20
+
+
+
+ AD Plugins
+ 807
+ 377
+ 396
+ 56
+ true
+
+ Label
+ Codec
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)Codec_RBV
+ 155
+ 0
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
+ AD Setup
+ 807
+ 438
+ 396
+ 231
+ true
+
+ Label
+ Port Name
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)PortName_RBV
+ 155
+ 0
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Manufacturer
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)Manufacturer_RBV
+ 155
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Model
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)Model_RBV
+ 155
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Serial Number
+ 0
+ 75
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)SerialNumber_RBV
+ 155
+ 75
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Firmware Version
+ 0
+ 100
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)FirmwareVersion_RBV
+ 155
+ 100
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ SDK Version
+ 0
+ 125
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)SDKVersion_RBV
+ 155
+ 125
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Driver Version
+ 0
+ 150
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)DriverVersion_RBV
+ 155
+ 150
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ AD Core Version
+ 0
+ 175
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ADCoreVersion_RBV
+ 155
+ 175
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
+ ND Circular Buff
+ 807
+ 674
+ 396
+ 131
+ true
+
+ Label
+ N Dimensions
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)NDimensions
+ 155
+ 0
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)NDimensions_RBV
+ 260
+ 0
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Dimensions
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextEntry
+ $(P)$(R)Dimensions
+ 155
+ 25
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(R)Dimensions_RBV
+ 260
+ 25
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Unique Id
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)UniqueId_RBV
+ 155
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Time Stamp
+ 0
+ 75
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)TimeStamp_RBV
+ 155
+ 75
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
+ ND Plugin Base Full
+ 807
+ 810
+ 396
+ 81
+ true
+
+ Label
+ Compressed Size
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)CompressedSize_RBV
+ 155
+ 0
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Bayer Pattern
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)BayerPattern_RBV
+ 155
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
+ NDROI
+ 1208
+ 30
+ 396
+ 56
+ true
+
+ Label
+ Array Size Z
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)ArraySizeZ_RBV
+ 155
+ 0
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
+ NDArrayBase Ungrouped
+ 1208
+ 91
+ 396
+ 106
+ true
+
+ Label
+ Acquire Busy CB
+ 0
+ 0
+ 150
+ 20
+ No description provided
+
+
+ LED
+ $(P)$(R)AcquireBusyCB
+ 247
+ 0
+ 20
+ 20
+
+
+ Label
+ Epics TS Sec
+ 0
+ 25
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)EpicsTSSec_RBV
+ 155
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Epics TS Nsec
+ 0
+ 50
+ 150
+ 20
+ No description provided
+
+
+ TextUpdate
+ $(P)$(R)EpicsTSNsec_RBV
+ 155
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
diff --git a/example/t01-services/bl01t-di-cam-01/index.bob b/example/t01-services/bl01t-di-cam-01/index.bob
new file mode 100644
index 00000000..e08e21bc
--- /dev/null
+++ b/example/t01-services/bl01t-di-cam-01/index.bob
@@ -0,0 +1,608 @@
+
+ bl01t-di-cam-01
+ 0
+ 0
+ 388
+ 530
+ 4
+ 4
+
+ Title
+ TITLE
+ bl01t-di-cam-01
+ 0
+ 0
+ 388
+ 25
+
+
+
+
+
+
+
+
+ true
+ 1
+
+
+ Label
+ ADAravis Camera
+ 23
+ 30
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ ADAravis.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :CAM:
+
+
+
+ BL01T-DI-CAM-01 :CAM:
+ 178
+ 30
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ GenICam 127.0.0.1
+ 23
+ 55
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ auto-10.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :CAM:
+
+
+
+ BL01T-DI-CAM-01 :CAM:
+ 178
+ 55
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDROI
+ 23
+ 80
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDROI.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :ROI:
+
+
+
+ BL01T-DI-CAM-01 :ROI:
+ 178
+ 80
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDStats
+ 23
+ 105
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDStats.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :STAT:
+
+
+
+ BL01T-DI-CAM-01 :STAT:
+ 178
+ 105
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDStdArrays
+ 23
+ 130
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDStdArrays.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :ARR:
+
+
+
+ BL01T-DI-CAM-01 :ARR:
+ 178
+ 130
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDProcess
+ 23
+ 155
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDProcess.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :PROC:
+
+
+
+ BL01T-DI-CAM-01 :PROC:
+ 178
+ 155
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlay
+ 23
+ 180
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlay.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:
+
+
+
+ BL01T-DI-CAM-01 :OVER:
+ 178
+ 180
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlayN
+ 23
+ 205
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlayN.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:1:
+
+
+
+ BL01T-DI-CAM-01 :OVER:1:
+ 178
+ 205
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlayN
+ 23
+ 230
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlayN.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:2:
+
+
+
+ BL01T-DI-CAM-01 :OVER:2:
+ 178
+ 230
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlayN
+ 23
+ 255
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlayN.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:3:
+
+
+
+ BL01T-DI-CAM-01 :OVER:3:
+ 178
+ 255
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlayN
+ 23
+ 280
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlayN.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:4:
+
+
+
+ BL01T-DI-CAM-01 :OVER:4:
+ 178
+ 280
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlayN
+ 23
+ 305
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlayN.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:5:
+
+
+
+ BL01T-DI-CAM-01 :OVER:5:
+ 178
+ 305
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlayN
+ 23
+ 330
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlayN.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:6:
+
+
+
+ BL01T-DI-CAM-01 :OVER:6:
+ 178
+ 330
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlayN
+ 23
+ 355
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlayN.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:7:
+
+
+
+ BL01T-DI-CAM-01 :OVER:7:
+ 178
+ 355
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDOverlayN
+ 23
+ 380
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDOverlayN.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :OVER:8:
+
+
+
+ BL01T-DI-CAM-01 :OVER:8:
+ 178
+ 380
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDFileTIFF
+ 23
+ 405
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDFileTIFF.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :TIFF:
+
+
+
+ BL01T-DI-CAM-01 :TIFF:
+ 178
+ 405
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDFileHDF5
+ 23
+ 430
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDFileHDF5.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :HDF5:
+
+
+
+ BL01T-DI-CAM-01 :HDF5:
+ 178
+ 430
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDColorConvert
+ 23
+ 455
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDColorConvert.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :CC:
+
+
+
+ BL01T-DI-CAM-01 :CC:
+ 178
+ 455
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ NDTransform
+ 23
+ 480
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ NDTransform.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :TRANS:
+
+
+
+ BL01T-DI-CAM-01 :TRANS:
+ 178
+ 480
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ ffmpegStream
+ 23
+ 505
+ 150
+ 20
+ No description provided
+
+
+ OpenDisplay
+
+
+ ffmpegStream.pvi.bob
+ tab
+ Open Display
+
+ BL01T-DI-CAM-01
+ :MJPG:
+
+
+
+ BL01T-DI-CAM-01 :MJPG:
+ 178
+ 505
+ 205
+ 20
+ $(actions)
+
+
diff --git a/example/t01-services/bl01t-mo-motor-01/index.bob b/example/t01-services/bl01t-mo-motor-01/index.bob
new file mode 100644
index 00000000..f4baccff
--- /dev/null
+++ b/example/t01-services/bl01t-mo-motor-01/index.bob
@@ -0,0 +1,123 @@
+
+ bl01t-mo-brick-01
+ 0
+ 0
+ 388
+ 230
+ 4
+ 4
+
+ Title
+ TITLE
+ bl01t-mo-brick-01
+ 0
+ 0
+ 388
+ 25
+
+
+
+
+
+
+
+
+ true
+ 1
+
+
+ Label
+ ppmacController
+ 23
+ 30
+ 150
+ 20
+ $(text)
+
+
+ OpenDisplay
+
+
+ ppmacController.pvi.bob
+ tab
+ Open Display
+
+ BL01T-MO-BRICK-01
+
+
+
+ BL01T-MO-BRICK-01
+ 178
+ 30
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ pmacAxis
+ 23
+ 55
+ 150
+ 20
+ $(text)
+
+
+ OpenDisplay
+
+
+ pmacAxis.pvi.bob
+ tab
+ Open Display
+
+ BL01T-MO-MOTOR-01
+ :X
+
+
+
+ BL01T-MO-MOTOR-01 :X
+ 178
+ 55
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ pmacAxis
+ 23
+ 80
+ 150
+ 20
+ $(text)
+
+
+ OpenDisplay
+
+
+ pmacAxis.pvi.bob
+ tab
+ Open Display
+
+ BL01T-MO-MOTOR-01
+ :A
+
+
+
+ BL01T-MO-MOTOR-01 :A
+ 178
+ 80
+ 205
+ 20
+ $(actions)
+
+
+ Label
+ pmacAxis
+ 23
+ 105
+ 150
+ 20
+ $(text)
+
+
diff --git a/example/t01-services/bl01t-mo-motor-01/pmacAxis.pvi.bob b/example/t01-services/bl01t-mo-motor-01/pmacAxis.pvi.bob
new file mode 100644
index 00000000..d5fa6540
--- /dev/null
+++ b/example/t01-services/bl01t-mo-motor-01/pmacAxis.pvi.bob
@@ -0,0 +1,1149 @@
+
+ pmacAxis
+ 0
+ 0
+ 807
+ 935
+ 4
+ 4
+
+ Title
+ TITLE
+ pmacAxis
+ 0
+ 0
+ 807
+ 25
+
+
+
+
+
+
+
+
+ true
+ 1
+
+
+ Motor
+ 5
+ 30
+ 396
+ 156
+ true
+
+ Label
+ Position
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M)
+ 155
+ 0
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P)$(M).RBV
+ 260
+ 0
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Stop
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ WritePV
+ $(P)$(M).STOP
+
+
+ $(pv_name)
+ 1
+ $(name)
+
+
+ Stop
+ 155
+ 25
+ 205
+ 20
+ $(P)$(M).STOP = 1
+
+
+ Label
+ Tweak Forward
+ 0
+ 50
+ 150
+ 20
+ $(text)
+
+
+ WritePV
+ $(P)$(M).TWF
+
+
+ $(pv_name)
+ 1
+ $(name)
+
+
+ Tweak Forward
+ 155
+ 50
+ 205
+ 20
+ $(P)$(M).TWF = 1
+
+
+ Label
+ Tweak Step
+ 0
+ 75
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).TWV
+ 155
+ 75
+ 205
+ 20
+ 1
+
+
+ Label
+ Tweak Reverse
+ 0
+ 100
+ 150
+ 20
+ $(text)
+
+
+ WritePV
+ $(P)$(M).TWR
+
+
+ $(pv_name)
+ 1
+ $(name)
+
+
+ Tweak Reverse
+ 155
+ 100
+ 205
+ 20
+ $(P)$(M).TWR = 1
+
+
+
+ Status
+ 5
+ 191
+ 396
+ 231
+ true
+
+ Label
+ Ready
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).DMOV
+ 247
+ 0
+ 20
+ 20
+
+
+ Label
+ Moving
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).MOVN
+ 247
+ 25
+ 20
+ 20
+
+
+ Label
+ Forward
+ 0
+ 50
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).TDIR
+ 247
+ 50
+ 20
+ 20
+
+
+ Label
+ Homed
+ 0
+ 75
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).MSTA
+ 247
+ 75
+ 20
+ 20
+
+
+ Label
+ High Limit
+ 0
+ 100
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).HLS
+ 247
+ 100
+ 20
+ 20
+
+
+ Label
+ Low Limit
+ 0
+ 125
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).LLS
+ 247
+ 125
+ 20
+ 20
+
+
+ Label
+ Soft Limit
+ 0
+ 150
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).LVIO
+ 247
+ 150
+ 20
+ 20
+
+
+ Label
+ Missed
+ 0
+ 175
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).MISS
+ 247
+ 175
+ 20
+ 20
+
+
+
+ Homing
+ 5
+ 427
+ 396
+ 81
+ true
+
+ Label
+ Home Forward
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ WritePV
+ $(P)$(M).HOMF
+
+
+ $(pv_name)
+ 1
+ $(name)
+
+
+ Home Forward
+ 155
+ 0
+ 205
+ 20
+ $(P)$(M).HOMF = 1
+
+
+ Label
+ Home Reverse
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ WritePV
+ $(P)$(M).HOMR
+
+
+ $(pv_name)
+ 1
+ $(name)
+
+
+ Home Reverse
+ 155
+ 25
+ 205
+ 20
+ $(P)$(M).HOMR = 1
+
+
+
+ Calibration
+ 5
+ 513
+ 396
+ 131
+ true
+
+ Label
+ Direction
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ ComboBox
+ $(P)$(M).DIR
+ 155
+ 0
+ 205
+ 20
+
+
+ Label
+ Offset
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).OFF
+ 155
+ 25
+ 205
+ 20
+ 1
+
+
+ Label
+ Freeze Offset
+ 0
+ 50
+ 150
+ 20
+ $(text)
+
+
+ ComboBox
+ $(P)$(M).FOFF
+ 155
+ 50
+ 205
+ 20
+
+
+ Label
+ Set Or Unset
+ 0
+ 75
+ 150
+ 20
+ $(text)
+
+
+ ComboBox
+ $(P)$(M).SET
+ 155
+ 75
+ 205
+ 20
+
+
+
+ Status Detail
+ 5
+ 649
+ 396
+ 281
+ true
+
+ Label
+ Status
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ BitField
+ $(P)$(M).MSTA
+ 155
+ 0
+ 200
+ 10
+
+
+ Label
+ Dial Read Back Value
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P)$(M).DRBV
+ 155
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Read Back Settle Time
+ 0
+ 50
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).DLY
+ 155
+ 50
+ 205
+ 20
+ 1
+
+
+ Label
+ Dval Minus Drbv
+ 0
+ 75
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P)$(M).DIFF
+ 155
+ 75
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Rval Minus Rrbv
+ 0
+ 100
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P)$(M).RDIF
+ 155
+ 100
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Raw Motor Position
+ 0
+ 125
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P)$(M).RRBV
+ 155
+ 125
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Raw Encoder Position
+ 0
+ 150
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P)$(M).REP
+ 155
+ 150
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Direction Of Travel
+ 0
+ 175
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P)$(M).TDIR
+ 155
+ 175
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Following Error
+ 0
+ 200
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P)$(M):FERROR
+ 155
+ 200
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Max Following Error
+ 0
+ 225
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P)$(M):FERRORMAX
+ 155
+ 225
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
+ Motion
+ 406
+ 30
+ 396
+ 331
+ true
+
+ Label
+ Max Velocity
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).VMAX
+ 155
+ 0
+ 205
+ 20
+ 1
+
+
+ Label
+ Base Velocity
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).VBAS
+ 155
+ 25
+ 205
+ 20
+ 1
+
+
+ Label
+ Velocity
+ 0
+ 50
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).VELO
+ 155
+ 50
+ 205
+ 20
+ 1
+
+
+ Label
+ Acceleration
+ 0
+ 75
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).ACCL
+ 155
+ 75
+ 205
+ 20
+ 1
+
+
+ Label
+ Jog Velocity
+ 0
+ 100
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).JVEL
+ 155
+ 100
+ 205
+ 20
+ 1
+
+
+ Label
+ Jog Acceleration
+ 0
+ 125
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).JAR
+ 155
+ 125
+ 205
+ 20
+ 1
+
+
+ Label
+ Backlash Distance
+ 0
+ 150
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).BDST
+ 155
+ 150
+ 205
+ 20
+ 1
+
+
+ Label
+ Backlash Velocity
+ 0
+ 175
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).BVEL
+ 155
+ 175
+ 205
+ 20
+ 1
+
+
+ Label
+ Backlash Secs To Vel
+ 0
+ 200
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).BACC
+ 155
+ 200
+ 205
+ 20
+ 1
+
+
+ Label
+ Move Fraction
+ 0
+ 225
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).FRAC
+ 155
+ 225
+ 205
+ 20
+ 1
+
+
+ Label
+ Rery Deadband
+ 0
+ 250
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).RDBD
+ 155
+ 250
+ 205
+ 20
+ 1
+
+
+ Label
+ Retry Count
+ 0
+ 275
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).RTRY
+ 155
+ 275
+ 205
+ 20
+ 1
+
+
+
+ Resolution
+ 406
+ 366
+ 396
+ 156
+ true
+
+ Label
+ Motor Step Size
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).MRES
+ 155
+ 0
+ 205
+ 20
+ 1
+
+
+ Label
+ Steps Per Revolution
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).SREV
+ 155
+ 25
+ 205
+ 20
+ 1
+
+
+ Label
+ Encoder Step Size
+ 0
+ 50
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).ERES
+ 155
+ 50
+ 205
+ 20
+ 1
+
+
+ Label
+ Readback Step Size
+ 0
+ 75
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).RRES
+ 155
+ 75
+ 205
+ 20
+ 1
+
+
+ Label
+ Use Encoder If Present
+ 0
+ 100
+ 150
+ 20
+ $(text)
+
+
+ ComboBox
+ $(P)$(M).UEIP
+ 155
+ 100
+ 205
+ 20
+
+
+
+ Motor Limits
+ 406
+ 527
+ 396
+ 256
+ true
+
+ Label
+ User High Limit
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).HLM
+ 155
+ 0
+ 205
+ 20
+ 1
+
+
+ Label
+ User Low Limit
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).LLM
+ 155
+ 25
+ 205
+ 20
+ 1
+
+
+ Label
+ Dial High Limit
+ 0
+ 50
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).DHLM
+ 155
+ 50
+ 205
+ 20
+ 1
+
+
+ Label
+ Dial Low Limit
+ 0
+ 75
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P)$(M).DLLM
+ 155
+ 75
+ 205
+ 20
+ 1
+
+
+ Label
+ Limit Violation
+ 0
+ 100
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).LVIO
+ 247
+ 100
+ 20
+ 20
+
+
+ Label
+ At High Limit Switch
+ 0
+ 125
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).HLS
+ 247
+ 125
+ 20
+ 20
+
+
+ Label
+ At Low Limit Switch
+ 0
+ 150
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).LLS
+ 247
+ 150
+ 20
+ 20
+
+
+ Label
+ At Raw High Limit Switch
+ 0
+ 175
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).RHLS
+ 247
+ 175
+ 20
+ 20
+
+
+ Label
+ At Raw Low Limit Switch
+ 0
+ 200
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P)$(M).RLLS
+ 247
+ 200
+ 20
+ 20
+
+
+
diff --git a/example/t01-services/bl01t-mo-motor-01/ppmacController.pvi.bob b/example/t01-services/bl01t-mo-motor-01/ppmacController.pvi.bob
new file mode 100644
index 00000000..ab03e76d
--- /dev/null
+++ b/example/t01-services/bl01t-mo-motor-01/ppmacController.pvi.bob
@@ -0,0 +1,1185 @@
+
+ ppmacController
+ 0
+ 0
+ 1172
+ 966
+ 4
+ 4
+
+ Title
+ TITLE
+ ppmacController
+ 0
+ 0
+ 1172
+ 25
+
+
+
+
+
+
+
+
+ true
+ 1
+
+
+ Pmac Controller
+ 5
+ 30
+ 761
+ 931
+ true
+
+ Label
+ Poll All Now
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ ToggleButton
+ $(P):PollAllNow
+ 155
+ 0
+ 205
+ 20
+
+
+ Label
+ Defer Moves
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ ToggleButton
+ $(P):DeferMoves
+ 155
+ 25
+ 205
+ 20
+
+
+ Label
+ Stop All
+ 0
+ 50
+ 150
+ 20
+ $(text)
+
+
+ ToggleButton
+ $(P):StopAll
+ 155
+ 50
+ 205
+ 20
+
+
+ Label
+ Kill All
+ 0
+ 75
+ 150
+ 20
+ $(text)
+
+
+ ToggleButton
+ $(P):KillAll
+ 155
+ 75
+ 205
+ 20
+
+
+ Label
+ FEEDRATELIMIT
+ 0
+ 100
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):FEEDRATE_LIMIT
+ 155
+ 100
+ 205
+ 20
+ 1
+
+
+ Label
+ REPORTFAST
+ 0
+ 125
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):REPORT_FAST
+ 155
+ 125
+ 205
+ 20
+ 1
+
+
+ Label
+ REPORTMEDIUM
+ 0
+ 150
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):REPORT_MEDIUM
+ 155
+ 150
+ 205
+ 20
+ 1
+
+
+ Label
+ REPORTSLOW
+ 0
+ 175
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):REPORT_SLOW
+ 155
+ 175
+ 205
+ 20
+ 1
+
+
+ Label
+ DEBUGLEVEL
+ 0
+ 200
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):DEBUG_LEVEL
+ 155
+ 200
+ 205
+ 20
+ 1
+
+
+ Label
+ DEBUGAXISNO
+ 0
+ 225
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):DEBUG_AXISNO
+ 155
+ 225
+ 205
+ 20
+ 1
+
+
+ Label
+ DEBUGCSNO
+ 0
+ 250
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):DEBUG_CSNO
+ 155
+ 250
+ 205
+ 20
+ 1
+
+
+ Label
+ DEBUGEXECUTE
+ 0
+ 275
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):DEBUG_EXECUTE
+ 155
+ 275
+ 205
+ 20
+ 1
+
+
+ Label
+ DISABLEPOLL
+ 0
+ 300
+ 150
+ 20
+ $(text)
+
+
+ ComboBox
+ $(P):DISABLE_POLL
+ 155
+ 300
+ 205
+ 20
+
+
+ Label
+ Send Cmd
+ 0
+ 325
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):SendCmd
+ 155
+ 325
+ 205
+ 20
+ 1
+ 6
+
+
+ Label
+ IVARIABLES
+ 0
+ 350
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):I_VARIABLES_RBV
+ 155
+ 350
+ 205
+ 20
+
+
+
+
+ 1
+ 6
+
+
+ Label
+ PVARIABLES
+ 0
+ 375
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):P_VARIABLES_RBV
+ 155
+ 375
+ 205
+ 20
+
+
+
+
+ 1
+ 6
+
+
+ Label
+ MVARIABLES
+ 0
+ 400
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):M_VARIABLES_RBV
+ 155
+ 400
+ 205
+ 20
+
+
+
+
+ 1
+ 6
+
+
+ Label
+ PMACTYPE
+ 0
+ 425
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):PMACTYPE
+ 155
+ 425
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ CPUNUMCORES
+ 0
+ 450
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):CPUNUMCORES
+ 155
+ 450
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ CPULOAD0
+ 0
+ 475
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):CPULOAD_0
+ 155
+ 475
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ CPULOAD1
+ 0
+ 500
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):CPULOAD_1
+ 155
+ 500
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ CPULOAD2
+ 0
+ 525
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):CPULOAD_2
+ 155
+ 525
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ CPULOAD3
+ 0
+ 550
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):CPULOAD_3
+ 155
+ 550
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READCTRLSTAT1
+ 0
+ 575
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_CTRLSTAT1
+ 155
+ 575
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READCTRLSTAT2
+ 0
+ 600
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_CTRLSTAT2
+ 155
+ 600
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READCTRLSTAT3
+ 0
+ 625
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_CTRLSTAT3
+ 155
+ 625
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READPLCBITS00
+ 0
+ 650
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_PLCBITS00
+ 155
+ 650
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READPLCBITS01
+ 0
+ 675
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_PLCBITS01
+ 155
+ 675
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READGPIOINP
+ 0
+ 700
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_GPIO_INP
+ 155
+ 700
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READGPIOOUT
+ 0
+ 725
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_GPIO_OUT
+ 155
+ 725
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READPROGBITS
+ 0
+ 750
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_PROGBITS
+ 155
+ 750
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ MACROERRS
+ 0
+ 775
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):MACROERRS
+ 155
+ 775
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ GLOBALPROBLEM
+ 0
+ 800
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P):GLOBAL_PROBLEM_RBV
+ 247
+ 800
+ 20
+ 20
+
+
+ Label
+ FEEDRATEPROBLEM
+ 0
+ 825
+ 150
+ 20
+ $(text)
+
+
+ LED
+ $(P):FEEDRATE_PROBLEM_RBV
+ 247
+ 825
+ 20
+ 20
+
+
+ Label
+ STATMSGNO
+ 0
+ 850
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_MSG_NO_RBV
+ 155
+ 850
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATTOTALWRITE
+ 0
+ 875
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_TOTAL_WRITE_RBV
+ 155
+ 875
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATTOTALREAD
+ 365
+ 0
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_TOTAL_READ_RBV
+ 520
+ 0
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATMSGWRITE
+ 365
+ 25
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_MSG_WRITE_RBV
+ 520
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATMSGREAD
+ 365
+ 50
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_MSG_READ_RBV
+ 520
+ 50
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATMSGTIME
+ 365
+ 75
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_MSG_TIME_RBV
+ 520
+ 75
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATMAXWRITE
+ 365
+ 100
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_MAX_WRITE_RBV
+ 520
+ 100
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATMAXREAD
+ 365
+ 125
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_MAX_READ_RBV
+ 520
+ 125
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATMAXTIME
+ 365
+ 150
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_MAX_TIME_RBV
+ 520
+ 150
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATAVEWRITE
+ 365
+ 175
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_AVE_WRITE_RBV
+ 520
+ 175
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATAVEREAD
+ 365
+ 200
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_AVE_READ_RBV
+ 520
+ 200
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ STATAVETIME
+ 365
+ 225
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):STAT_AVE_TIME_RBV
+ 520
+ 225
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ FASTSIZE
+ 365
+ 250
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):FAST_SIZE_RBV
+ 520
+ 250
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ MEDIUMSIZE
+ 365
+ 275
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):MEDIUM_SIZE_RBV
+ 520
+ 275
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ SLOWSIZE
+ 365
+ 300
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):SLOW_SIZE_RBV
+ 520
+ 300
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ COORDINATESYSGROUP
+ 365
+ 325
+ 150
+ 20
+ $(text)
+
+
+ ComboBox
+ $(P):COORDINATE_SYS_GROUP
+ 520
+ 325
+ 100
+ 20
+
+
+ TextUpdate
+ $(P):COORDINATE_SYS_GROUP_RBV
+ 625
+ 325
+ 100
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ Axes Readonly
+ 365
+ 350
+ 150
+ 20
+ $(text)
+
+
+ ToggleButton
+ $(P):AxesReadonly
+ 520
+ 350
+ 100
+ 20
+
+
+ LED
+ $(P):AxesReadonly_RBV
+ 665
+ 350
+ 20
+ 20
+
+
+ Label
+ FEEDRATE
+ 365
+ 375
+ 150
+ 20
+ $(text)
+
+
+ TextEntry
+ $(P):FEEDRATE
+ 520
+ 375
+ 100
+ 20
+ 1
+
+
+ TextUpdate
+ $(P):FEEDRATE_RBV
+ 625
+ 375
+ 100
+ 20
+
+
+
+
+ 1
+
+
+
+ Power Pmac Status
+ 771
+ 30
+ 396
+ 81
+ true
+
+ Label
+ SERVOPERIOD
+ 0
+ 0
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):SERVO_PERIOD
+ 155
+ 0
+ 205
+ 20
+
+
+
+
+ 1
+
+
+ Label
+ READECHO
+ 0
+ 25
+ 150
+ 20
+ $(text)
+
+
+ TextUpdate
+ $(P):READ_ECHO
+ 155
+ 25
+ 205
+ 20
+
+
+
+
+ 1
+
+
+
diff --git a/example/t01-services/synoptic/JsonMap.json b/example/t01-services/synoptic/JsonMap.json
index 7b3732ae..061b331b 100644
--- a/example/t01-services/synoptic/JsonMap.json
+++ b/example/t01-services/synoptic/JsonMap.json
@@ -67,7 +67,7 @@
"file": "motor1.bob",
"children": [
{
- "file": "$(IOC)/pmacAxis.pvi.bob",
+ "file": "../bl01t-mo-motor-01/pmacAxis.pvi.bob",
"macros": {
"M": ":X",
"P": "BL01T-MO-MOTOR-01",
@@ -77,7 +77,7 @@
"displayName": "X1"
},
{
- "file": "$(IOC)/pmacAxis.pvi.bob",
+ "file": "../bl01t-mo-motor-01/pmacAxis.pvi.bob",
"macros": {
"M": ":A",
"P": "BL01T-MO-MOTOR-01",
diff --git a/src/techui_builder/generate_jsonmap.py b/src/techui_builder/generate_jsonmap.py
index e8db9447..bb4846e4 100644
--- a/src/techui_builder/generate_jsonmap.py
+++ b/src/techui_builder/generate_jsonmap.py
@@ -1,5 +1,6 @@
import json
import logging
+import re
from collections import defaultdict
from dataclasses import _MISSING_TYPE, dataclass, field
from pathlib import Path
@@ -11,10 +12,12 @@
from lxml.objectify import ObjectifiedElement
from techui_builder._logger import Logger
-from techui_builder.models import TechUi
+from techui_builder.models import Component, TechUi
logger_ = logging.getLogger(__name__)
+_PVI_FILE_RE = re.compile(r"^(?:\$\(IOC\))\/([a-zA-Z]+[.a-zA-Z]+)$")
+
def log_level(level: str):
Logger(level)
@@ -54,6 +57,7 @@ class JsonMapGenerator:
bob_path: Path = field(default=Path("index.bob"))
techui: Path = field(default=Path("techui.yaml"))
output: Path | None = field(default=None)
+ _current_service_name: str = field(default="", init=False, repr=False)
def __post_init__(self):
# Determine the directory to write the json map file to.
@@ -68,7 +72,7 @@ def __post_init__(self):
self.techui == self.__class__.__dataclass_fields__["techui"].default
and not self.techui.exists()
):
- self.techui = self._parent_path.joinpath("techui.yaml")
+ self.techui = self._parent_path / "techui.yaml"
try:
self.techui_yaml: TechUi = TechUi.model_validate(
yaml.safe_load(self.techui.read_text(encoding="utf-8"))
@@ -102,7 +106,7 @@ def _get_display_name(
display_name = self._parse_display_name(display_name, file_path)
return display_name
- def _next_file_crawl(
+ def _child_file_crawl(
file_path_text: str,
destination_path: Path,
name_element: str | None,
@@ -111,13 +115,21 @@ def _next_file_crawl(
macro_dictionary: dict[str, Any],
):
# TODO: misleading var name?
- next_file_path = destination_path.joinpath(file_path_text)
+ child_file_path = destination_path / file_path_text
+
+ match = _PVI_FILE_RE.fullmatch(file_path_text)
+ # The file path is a PVI screen, so attempt to find that screen
+ if match:
+ file_name = match.group(1)
+ child_file_path = (
+ destination_path / f"../{self._current_service_name}/{file_name}"
+ )
# Crawl the next file
- if next_file_path.is_file():
+ if child_file_path.is_file():
# TODO: investigate non-recursive approaches?
child_node = self.generate_json_map(
- next_file_path,
+ child_file_path,
destination_path,
current_component_name=component_name,
name_elem=name_element,
@@ -135,7 +147,11 @@ def _next_file_crawl(
# Create initial node at top of .bob file
current_node = JsonMap(
- str(screen_path.resolve().relative_to(self._parent_path.resolve())),
+ str(
+ screen_path.resolve().relative_to(
+ self._parent_path.resolve(), walk_up=True
+ )
+ ),
display_name=None,
)
@@ -145,6 +161,11 @@ def _next_file_crawl(
and screen_path.stem in self.techui_yaml.components
):
current_component_name = screen_path.stem
+ # We know from the if statement that it exists
+ _current_component = self.techui_yaml.components.get(current_component_name)
+ assert isinstance(_current_component, Component)
+ # TODO: How to find the screens if PV prefix is not the service name???
+ self._current_service_name = _current_component.prefix.lower()
abs_path = screen_path.absolute()
@@ -214,7 +235,7 @@ def _next_file_crawl(
name_elem, current_component_name, file_path
)
- child_node = _next_file_crawl(
+ child_node = _child_file_crawl(
file_text,
dest_path,
name_elem,
@@ -249,7 +270,7 @@ def _next_file_crawl(
name_elem, current_component_name, file_path
)
- child_node = _next_file_crawl(
+ child_node = _child_file_crawl(
file_text,
dest_path,
name_elem,
diff --git a/tests/conftest.py b/tests/conftest.py
index 5e22ec61..964c008e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -236,6 +236,59 @@ def example_display_names_json():
return test_map
+@pytest.fixture
+def example_json_map_pvi_screens():
+ jsonmap = JsonMap(
+ file="motor1.bob",
+ display_name="motor1",
+ exists=True,
+ duplicate=False,
+ children=[
+ JsonMap(
+ file="../bl01t-mo-motor-01/pmacAxis.pvi.bob",
+ display_name="X1",
+ exists=True,
+ duplicate=False,
+ children=[],
+ macros={
+ "M": ":X",
+ "P": "BL01T-MO-MOTOR-01",
+ "label": "X1",
+ "IOC": "https://t01-opis.diamond.ac.uk/bl01t-mo-motor-01",
+ },
+ error="",
+ ),
+ JsonMap(
+ file="../bl01t-mo-motor-01/pmacAxis.pvi.bob",
+ display_name="A",
+ exists=True,
+ duplicate=False,
+ children=[],
+ macros={
+ "M": ":A",
+ "P": "BL01T-MO-MOTOR-01",
+ "label": "A",
+ "IOC": "https://t01-opis.diamond.ac.uk/bl01t-mo-motor-01",
+ },
+ error="",
+ ),
+ JsonMap(
+ file="techui-support/bob/pmac/pmacController.bob",
+ display_name="pmacController",
+ exists=True,
+ duplicate=False,
+ children=[],
+ macros={"P": "BL01T-MO-BRICK-01"},
+ error="",
+ ),
+ ],
+ macros={},
+ error="",
+ )
+
+ return jsonmap
+
+
@pytest.fixture
def generator(techui_support, tmp_t01_services):
synoptic_dir = tmp_t01_services / "synoptic"
@@ -289,6 +342,64 @@ def example_xml_embedded_widget(tmp_test_files):
return widget_element
+@pytest.fixture
+def example_xml_embedded_widget_pvi_child(tmp_t01_services):
+ # You cannot set a text tag of an ObjectifiedElement,
+ # so we need to make an etree.Element and convert it ...
+
+ widget_element = Element("widget")
+ widget_element.set("type", "embedded")
+ widget_element.set("version", "2.0.0")
+ name_element = SubElement(widget_element, "name")
+ name_element.text = "motor"
+ width_element = SubElement(widget_element, "width")
+ width_element.text = "205"
+ height_element = SubElement(widget_element, "height")
+ height_element.text = "120"
+ file_element = SubElement(widget_element, "file")
+ file_element.text = str(tmp_t01_services / "mock_adaravis_service/ADAravis.pvi.bob")
+ macros_element = SubElement(widget_element, "macros")
+ macro_element_1 = SubElement(macros_element, "macro1")
+ macro_element_1.text = "test_macro_1"
+
+ # ... which requires this horror
+ widget_element = fromstring(tostring(widget_element))
+
+ return widget_element
+
+
+@pytest.fixture
+def example_xml_related_widget_pvi_child(tmp_t01_services):
+ # You cannot set a text tag of an ObjectifiedElement,
+ # so we need to make an etree.Element and convert it ...
+
+ widget_element = Element("widget")
+ widget_element.set("type", "action_button")
+ widget_element.set("version", "2.0.0")
+ name_element = SubElement(widget_element, "name")
+ name_element.text = "motor"
+ width_element = SubElement(widget_element, "width")
+ width_element.text = "205"
+ height_element = SubElement(widget_element, "height")
+ height_element.text = "120"
+
+ actions_element = SubElement(widget_element, "actions")
+ action_element = SubElement(actions_element, "action")
+ action_element.set("type", "open_display")
+ file_element = SubElement(action_element, "file")
+ file_element.text = str(tmp_t01_services / "mock_adaravis_service/ADAravis.pvi.bob")
+ desc_element = SubElement(action_element, "description")
+ desc_element.text = "placeholder description"
+ macros_element = SubElement(action_element, "macros")
+ macro_element = SubElement(macros_element, "P")
+ macro_element.text = "placeholder P"
+
+ # ... which requires this horror
+ widget_element = fromstring(tostring(widget_element))
+
+ return widget_element
+
+
@pytest.fixture
def example_xml_related_widget(tmp_t01_services):
# You cannot set a text tag of an ObjectifiedElement,
diff --git a/tests/test_generate_jsonmap.py b/tests/test_generate_jsonmap.py
index 923d0aad..6c508891 100644
--- a/tests/test_generate_jsonmap.py
+++ b/tests/test_generate_jsonmap.py
@@ -166,6 +166,17 @@ def test_generate_json_map_nav_tabs(
assert test_json_map == example_json_map_root
+def test_generate_json_map_child_file_crawl_pvi_screen(
+ json_map_generator, example_json_map_pvi_screens, tmp_t01_services
+):
+ jsonmap = json_map_generator.generate_json_map(
+ screen_path=tmp_t01_services / "synoptic/motor1.bob",
+ dest_path=tmp_t01_services / "synoptic",
+ )
+
+ assert example_json_map_pvi_screens == jsonmap
+
+
def test_parse_display_name_with_name(json_map_generator):
"""Test parse display name when tag is present"""
display_name = json_map_generator._parse_display_name(