Many more scans
This commit is contained in:
@@ -14,27 +14,35 @@ as a special beamline console with the AWI software stack and is administered by
|
||||
services are set up such that the *gac-x05la* account can administer all services.
|
||||
As any beamline console it's part of the *sls-bl-x05la* beamline network, so you have to go through
|
||||
the SSH gateway by:
|
||||
'''
|
||||
```
|
||||
ssh -J x05la-gw gac-x05la@x05la-bec-001
|
||||
'''
|
||||
```
|
||||
PSI's virtual terminal service *nx-term.psi.ch* offers a convenient way to have a linux desktop
|
||||
from your browser.
|
||||
|
||||
### Monitoring BEC services
|
||||
|
||||
Once logged in to the *x05la-bec-001* host, you can attach to the console of the BEC services by:
|
||||
'''
|
||||
```
|
||||
tmux attach -t bec
|
||||
'''
|
||||
```
|
||||
You can restart services by clicking on their sub-terminal and pressing Ctrl + C (sometimes twice).
|
||||
This is needed whenever you edit the scan or device files.
|
||||
|
||||
### Activating the BEC environment
|
||||
|
||||
In order to start the BEC console interface, you first need to activate the corresponding venv.
|
||||
This will also start the BEC GUI in a separate process and window:
|
||||
```
|
||||
source /data/test/x05la-test-bec/bec_deployment/bev_venv/bin/activate
|
||||
bec
|
||||
```
|
||||
|
||||
### The BEC deployment
|
||||
|
||||
The BEC is deployed to a beamline specific network mounted folder that contains the main
|
||||
repositories for the current installation, you can check it by:
|
||||
'''
|
||||
```
|
||||
[gac-x05la@x05la-bec-001 ~]$ ls -ltr /data/test/x05la-test-bec/bec_deployment/
|
||||
total 4
|
||||
drwxr-xr-x 7 gac-x05la unx-sls 4096 Aug 20 15:10 ophyd_devices
|
||||
@@ -45,7 +53,7 @@ drwxr-xr-x 6 gac-x05la unx-sls 4096 Aug 20 15:11 bec_venv
|
||||
drwxr-xr-x 15 gac-x05la unx-sls 4096 Aug 29 13:06 bec
|
||||
drwxr-xr-x 7 gac-x05la unx-sls 4096 Sep 3 13:28 tomcat_bec
|
||||
drwxr-xr-x 2 gac-x05la unx-sls 4096 Sep 6 11:22 logs
|
||||
'''
|
||||
```
|
||||
A short summary of the folder structure:
|
||||
- bec_venv : The actual installation directory of the BEC framework
|
||||
- bec : Git repo for the BEC framework
|
||||
@@ -55,18 +63,10 @@ A short summary of the folder structure:
|
||||
|
||||
In short: You should use the bec_venv and only edit the tomcat_bec folder. And this is easily done
|
||||
in Visual Studio Code, a really handy IDE if you have a fast connection:
|
||||
'''
|
||||
```
|
||||
code &
|
||||
'''
|
||||
```
|
||||
|
||||
### The BEC environment
|
||||
|
||||
So in order to start the BEC console interface, you first need to activate the venv. This will
|
||||
also start the BEC GUI in a separate process and window:
|
||||
'''
|
||||
source /data/test/x05la-test-bec/bec_deployment/bev_venv/bin/activate
|
||||
bec
|
||||
'''
|
||||
|
||||
|
||||
### The Tomcat BEC repository
|
||||
@@ -75,7 +75,7 @@ The Tomcat BEC repository is a collection of Tomcat specific ophyd devices, scan
|
||||
device configurations. It tries to enforce a standard structure across beamlines and it's
|
||||
**maintenance is the responsibility of the beamline.** I.e. it should be handed over from
|
||||
controls while still keeping the basic structure:
|
||||
'''
|
||||
```
|
||||
[gac-x05la@x05la-bec-001 ~]$ ls -ltr /data/test/x05la-test-bec/bec_deployment/tomcat_bec/tomcat_bec/
|
||||
total 6
|
||||
-rw-r--r-- 1 gac-x05la unx-sls 0 Jan 1 1970 __init__.py
|
||||
@@ -90,30 +90,30 @@ drwxr-xr-x 3 gac-x05la unx-sls 4096 Sep 3 13:28 device_configs
|
||||
drwxr-xr-x 7 gac-x05la unx-sls 4096 Sep 5 18:08 devices
|
||||
drwxr-xr-x 3 gac-x05la unx-sls 4096 Sep 16 12:30 scans
|
||||
-rw-r--r-- 1 gac-x05la unx-nogroup 3383 Oct 1 16:08 Readme.md
|
||||
'''
|
||||
```
|
||||
From the list of folders the most important ones are:
|
||||
- device_configs : Ophyd device datbase as YAML file
|
||||
- devices : Ophyd device repository (i.e. Aerotech, Gigafrost)
|
||||
- scans : Tomcat specific base scans in the low-level API
|
||||
|
||||
The official location of the beamline repository is:
|
||||
'''
|
||||
```
|
||||
https://gitlab.psi.ch/bec/tomcat_bec
|
||||
'''
|
||||
```
|
||||
|
||||
|
||||
### Configuration and basic diagnostic in BEC
|
||||
|
||||
Once you're at the CLI it is good practice to check the available devices:
|
||||
'''
|
||||
```
|
||||
dev.show_all()
|
||||
'''
|
||||
```
|
||||
If the previous two configuration attempts were unsuccesfull the bec_device_server will start
|
||||
without any devices. A single missing or mistyped IOC will cause a configuration failure, so once
|
||||
you fixed you configs you can reload your YAML config file:
|
||||
'''
|
||||
```
|
||||
bec.config.update_session_with_file("/data/test/x05la-test-bec/bec_deployment/tomcat_bec/tomcat_bec/device_configs/microxas_test_bed.yaml")
|
||||
'''
|
||||
```
|
||||
If a single, unnecesary device doesn't come up , just comment it out.
|
||||
|
||||
|
||||
@@ -136,25 +136,24 @@ recently updated event model. Redundant staging raises an exception.
|
||||
Since seeing is believing, it's often a good idea to bring up the panels and get some visual
|
||||
feedback. You can start the standard CaQtDM panels for the Aerotech and GigaFrost from any
|
||||
beamline console running the contros stack by:
|
||||
'''
|
||||
```
|
||||
caqtdm -macro "CAM=X02DA-CAM-GF2" X_X02DA_GIGAFROST_camControl_user.ui
|
||||
caqtdm -macro "P=X02DA-ES1-SMP1" aeroauto1_ControllerOverview.ui
|
||||
'''
|
||||
```
|
||||
|
||||
The std-DAQ doesn't have control panels, but the BEC can display the hard-coded preview streams.
|
||||
The *daq_stream0* preview forwards displays 2 frames out of every 555 images. The second stream
|
||||
is too fast for the bec_device_server and is thus disabled.
|
||||
The *daq_stream1* preview forwards displays 1 frames at a maximum of 5 Hz.
|
||||
So we just need to add the correct widgets to the GUI:
|
||||
'''
|
||||
```
|
||||
gui.add_dock('tomolive')
|
||||
gui.panels['tomolive'].add_widget('BECFigure').image('daq_stream0')
|
||||
'''
|
||||
gui.panels['tomolive'].add_widget('BECFigure').image('daq_stream1')
|
||||
```
|
||||
|
||||
### Short run with the GigaFrost and std-DAQ
|
||||
|
||||
This is a short, recorded test run with 2016x2016 frames at 100 Hz with the GigaFrost. The current
|
||||
GigaFrost ophyd device includes the std-DAQ client, but they will probably be separated.
|
||||
'''
|
||||
```
|
||||
# Configure
|
||||
d = {'ntotal': 1000000, 'nimages': 555, 'exposure': 5, 'period': 10}
|
||||
dev.gfclient.configure(d)
|
||||
@@ -167,14 +166,14 @@ def.gfclient.trigger()
|
||||
# Unstage
|
||||
def.gfclient.unstage()
|
||||
dev.daq_stream0.unstage()
|
||||
'''
|
||||
```
|
||||
|
||||
## Reseting the Automation1 iSMC
|
||||
|
||||
There's some electrical disturbance at SLS that can bring the system to a problematic state. A workaround is to remotely reset the iSMC:
|
||||
'''
|
||||
```
|
||||
dev.es1_ismc.reset()
|
||||
'''
|
||||
```
|
||||
|
||||
FIXME: There is a known desync in PSO inputs upon iSMC restart that requires restarting the IOC.
|
||||
|
||||
@@ -185,9 +184,9 @@ FIXME: There is a known desync in PSO inputs upon iSMC restart that requires res
|
||||
As I was unsure about the various triggering modes of the Gigafrost, I only prepared a simple
|
||||
step scanning implementation that is compatible with the standard bluesky step scanning interface
|
||||
using soft-triggers
|
||||
'''
|
||||
```
|
||||
scans.simplestep(range=(-20, 160), steps=18, exp_time=2, exp_burst=220)
|
||||
'''
|
||||
```
|
||||
|
||||
### Templated script scans
|
||||
|
||||
|
||||
@@ -95,7 +95,6 @@ es1_psod:
|
||||
prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'
|
||||
deviceTags:
|
||||
- es1
|
||||
- trigger
|
||||
enabled: true
|
||||
onFailure: buffer
|
||||
readOnly: false
|
||||
|
||||
@@ -985,6 +985,15 @@ class aa1AxisDriveDataCollection(Device):
|
||||
super().unstage()
|
||||
print(f"Recorded samples: {self.nsamples_rbv.value}")
|
||||
|
||||
def kickoff(self) -> DeviceStatus:
|
||||
if not self._staged:
|
||||
self.stage()
|
||||
#status = DeviceStatus(self, done=True, success=True, settle_time=0.1)
|
||||
status = DeviceStatus(self)
|
||||
status.set_finished()
|
||||
return status
|
||||
|
||||
|
||||
def complete(self, settle_time=0.1) -> DeviceStatus:
|
||||
"""DDC just reads back whatever is available in the buffers"""
|
||||
sleep(settle_time)
|
||||
|
||||
@@ -106,7 +106,7 @@ class GigaFrostClient(PSIDetectorBase):
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
custom_prepare_cls = GigaFrostClientMixin
|
||||
USER_ACCESS = [""]
|
||||
USER_ACCESS = ["kickoff"]
|
||||
|
||||
cam = Component(gfcam.GigaFrostCamera, prefix="X02DA-CAM-GF2:", name="cam")
|
||||
daq = Component(stddaq.StdDaqClient, name="daq")
|
||||
@@ -182,6 +182,11 @@ class GigaFrostClient(PSIDetectorBase):
|
||||
|
||||
return super().stage()
|
||||
|
||||
def kickoff(self) -> DeviceStatus:
|
||||
if not self._staged:
|
||||
self.stage()
|
||||
return DeviceStatus(self, done=True, success=True, settle_time=0.1)
|
||||
|
||||
# def trigger(self) -> DeviceStatus:
|
||||
# """ Triggers the current device and all sub-devices, i.e. the camera.
|
||||
# """
|
||||
|
||||
@@ -9,7 +9,7 @@ Created on Thu Jun 27 17:28:43 2024
|
||||
import json
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
from ophyd import Device, Signal, Component, Kind
|
||||
from ophyd import Device, Signal, Component, Kind, DeviceStatus
|
||||
from websockets.sync.client import connect
|
||||
from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
|
||||
|
||||
@@ -43,6 +43,7 @@ class StdDaqClient(Device):
|
||||
'''
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
USER_ACCESS=["kickoff"]
|
||||
|
||||
# Status attributes
|
||||
url = Component(Signal, kind=Kind.config)
|
||||
@@ -189,6 +190,10 @@ class StdDaqClient(Device):
|
||||
pass
|
||||
return super().unstage()
|
||||
|
||||
def kickoff(self) -> DeviceStatus:
|
||||
""" The DAQ was not meant to quickly toggle"""
|
||||
return DeviceStatus(self, done=True, success=True, settle_time=0.1)
|
||||
|
||||
def stop(self, *, success=False):
|
||||
""" Stop a running acquisition
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ from time import sleep, time
|
||||
from threading import Thread
|
||||
import zmq
|
||||
import numpy as np
|
||||
from ophyd import Device, Signal, Component, Kind
|
||||
from ophyd import Device, Signal, Component, Kind, DeviceStatus
|
||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
||||
CustomDetectorMixin,
|
||||
PSIDetectorBase,
|
||||
@@ -134,6 +134,7 @@ class StdDaqPreviewDetector(PSIDetectorBase):
|
||||
cam_widget = gui.add_dock('cam_dock1').add_widget('BECFigure').image('daq_stream1')
|
||||
"""
|
||||
# Subscriptions for plotting image
|
||||
USER_ACCESS=["kickoff"]
|
||||
SUB_MONITOR = "device_monitor_2d"
|
||||
_default_sub = SUB_MONITOR
|
||||
|
||||
@@ -179,6 +180,9 @@ class StdDaqPreviewDetector(PSIDetectorBase):
|
||||
sleep(1)
|
||||
self._socket.connect(self.url.get())
|
||||
|
||||
def kickoff(self) -> DeviceStatus:
|
||||
""" The DAQ was not meant to be toggled"""
|
||||
return DeviceStatus(self, done=True, success=True, settle_time=0.1)
|
||||
|
||||
# Automatically connect to MicroSAXS testbench if directly invoked
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -157,12 +157,14 @@ class TemplatedScanBase(AsyncFlyScanBase):
|
||||
|
||||
# Collect
|
||||
if self.daqname is not None and self.daqmode=="collect":
|
||||
print("TOMCAT Collecting scripted scan results (from EPICS)")
|
||||
positions = yield from self.stubs.send_rpc_and_wait(self.daqname, "collect")
|
||||
logger.info(f"Finished scan with collected positions: {positions}")
|
||||
|
||||
def cleanup(self):
|
||||
"""Set scan progress to 1 to finish the scan"""
|
||||
self.num_pos = 1
|
||||
print("TOMCAT Officially finshed the scan")
|
||||
return super().cleanup()
|
||||
|
||||
|
||||
@@ -520,10 +522,10 @@ class SequenceScanBase(TemplatedScanBase):
|
||||
# Call super() to do the substitutions
|
||||
yield from super().prepare_positions()
|
||||
|
||||
def unstage(self):
|
||||
""" Wait for DAQ before unstaging"""
|
||||
time.sleep(1)
|
||||
yield from super().unstage()
|
||||
# def unstage(self):
|
||||
# """ Wait for DAQ before unstaging"""
|
||||
# time.sleep(1)
|
||||
# yield from super().unstage()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ def demosequencescan(scan_start, gate_high, gate_low, repeats=1, repmode="PosNeg
|
||||
scans.sequencescan(scan_start, gate_high, gate_low, exp_time=exp_time, exp_frames=exp_frames, repeats=repeats, repmode=repmode)
|
||||
|
||||
|
||||
def becsequencescan(scan_start, gate_high, gate_low, repeats=1, repmode="PosNeg",
|
||||
def becsequencescan(start, gate_high, gate_low, repeats=1, repmode="PosNeg",
|
||||
exp_time=0.005, exp_frames=180, sync='pso'):
|
||||
""" Demo sequence scan with GigaFrost
|
||||
|
||||
@@ -106,15 +106,19 @@ def becsequencescan(scan_start, gate_high, gate_low, repeats=1, repmode="PosNeg"
|
||||
if scan_repmode in ("POS", "NEG"):
|
||||
scan_range = repeats *(gate_high + gate_low)
|
||||
if scan_repmode=="POS":
|
||||
scan_end = scan_start + scan_range + scan_accdistance
|
||||
scan_start = start - scan_accdistance
|
||||
scan_end = start + scan_range + scan_accdistance
|
||||
if scan_repmode=="NEG":
|
||||
scan_end = scan_start - scan_range - scan_accdistance
|
||||
scan_start = start + scan_accdistance
|
||||
scan_end = start - scan_range - scan_accdistance
|
||||
elif scan_repmode in ("POSNEG", "NEGPOS"):
|
||||
scan_range = gate_high + gate_low
|
||||
if scan_repmode=="POSNEG":
|
||||
scan_end = scan_start + scan_range + scan_accdistance
|
||||
scan_start = start - scan_accdistance
|
||||
scan_end = start + scan_range + scan_accdistance
|
||||
if scan_repmode=="NEGPOS":
|
||||
scan_end = scan_start - scan_range - scan_accdistance
|
||||
scan_start = start + scan_accdistance
|
||||
scan_end = start - scan_range - scan_accdistance
|
||||
else:
|
||||
raise RuntimeError(f"Unsupported repetition mode: {repmode}")
|
||||
|
||||
@@ -165,35 +169,40 @@ def becsequencescan(scan_start, gate_high, gate_low, repeats=1, repmode="PosNeg"
|
||||
dev.es1_ddaq.configure(daqcfg)
|
||||
dev.es1_psod.configure(psocfg)
|
||||
|
||||
# Stage all devices
|
||||
dev.gfclient.stage()
|
||||
dev.es1_ddaq.stage()
|
||||
dev.es1_psod.stage()
|
||||
dev.daq_stream1.stage()
|
||||
try:
|
||||
# Stage all devices
|
||||
dev.gfclient.stage()
|
||||
dev.es1_ddaq.stage()
|
||||
dev.es1_psod.stage()
|
||||
dev.daq_stream1.stage()
|
||||
|
||||
# Kick off devices
|
||||
dev.gfclient.kickoff()
|
||||
dev.es1_ddaq.kickoff()
|
||||
dev.es1_psod.kickoff()
|
||||
print("Handing over to 'scans.line_scan'")
|
||||
# Kick off devices
|
||||
dev.gfclient.kickoff()
|
||||
dev.es1_ddaq.kickoff()
|
||||
dev.es1_psod.kickoff()
|
||||
|
||||
if scan_repmode in ["POS", "NEG"]:
|
||||
dev.es1_psod.prepare(psoBoundsPos)
|
||||
dev.es1_roty.move(scan_end).wait()
|
||||
elif scan_repmode in ["POSNEG", "NEGPOS"]:
|
||||
for ii in range(scan_repnum):
|
||||
if ii%2==0:
|
||||
print("Manual motor scan")
|
||||
if scan_repmode in ["POS", "NEG"]:
|
||||
dev.es1_psod.prepare(psoBoundsPos)
|
||||
dev.es1_roty.move(scan_end).wait()
|
||||
if ii%2==1:
|
||||
dev.es1_psod.prepare(psoBoundsNeg)
|
||||
dev.es1_roty.move(scan_start).wait()
|
||||
|
||||
# Stage all devices
|
||||
dev.gfclient.unstage()
|
||||
dev.es1_ddaq.unstage()
|
||||
dev.es1_psod.unstage()
|
||||
dev.daq_stream1.unstage()
|
||||
elif scan_repmode in ["POSNEG", "NEGPOS"]:
|
||||
for ii in range(scan_repnum):
|
||||
if ii%2==0:
|
||||
dev.es1_psod.prepare(psoBoundsPos)
|
||||
# FIXME : Temporary trigger
|
||||
dev.gfclient.trigger()
|
||||
dev.es1_roty.move(scan_end).wait()
|
||||
if ii%2==1:
|
||||
dev.es1_psod.prepare(psoBoundsNeg)
|
||||
# FIXME : Temporary trigger
|
||||
dev.gfclient.trigger()
|
||||
dev.es1_roty.move(scan_start).wait()
|
||||
finally:
|
||||
# Stage all devices
|
||||
dev.gfclient.unstage()
|
||||
dev.es1_ddaq.unstage()
|
||||
dev.es1_psod.unstage()
|
||||
dev.daq_stream1.unstage()
|
||||
|
||||
# Move back to start
|
||||
dev.es1_roty.move(scan_start).wait()
|
||||
|
||||
Reference in New Issue
Block a user