From 7118b2da2b5a0261e1da0fe5fa78d3fefb226f41 Mon Sep 17 00:00:00 2001 From: gac-x05la Date: Fri, 18 Oct 2024 16:10:29 +0200 Subject: [PATCH] Many more scans --- tomcat_bec/Readme.md | 73 +++++++++---------- .../device_configs/microxas_test_bed.yaml | 1 - .../devices/aerotech/AerotechAutomation1.py | 9 +++ .../devices/gigafrost/gigafrostclient.py | 7 +- tomcat_bec/devices/gigafrost/stddaq_client.py | 7 +- .../devices/gigafrost/stddaq_preview.py | 6 +- tomcat_bec/scans/tomcat_scanbase.py | 10 ++- tomcat_bec/scripts/demoscans.py | 69 ++++++++++-------- 8 files changed, 107 insertions(+), 75 deletions(-) diff --git a/tomcat_bec/Readme.md b/tomcat_bec/Readme.md index f7a53aa..68d0d35 100644 --- a/tomcat_bec/Readme.md +++ b/tomcat_bec/Readme.md @@ -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 diff --git a/tomcat_bec/device_configs/microxas_test_bed.yaml b/tomcat_bec/device_configs/microxas_test_bed.yaml index 90d835c..15f36e3 100644 --- a/tomcat_bec/device_configs/microxas_test_bed.yaml +++ b/tomcat_bec/device_configs/microxas_test_bed.yaml @@ -95,7 +95,6 @@ es1_psod: prefix: 'X02DA-ES1-SMP1:ROTY:PSO:' deviceTags: - es1 - - trigger enabled: true onFailure: buffer readOnly: false diff --git a/tomcat_bec/devices/aerotech/AerotechAutomation1.py b/tomcat_bec/devices/aerotech/AerotechAutomation1.py index 93f56a0..3678458 100644 --- a/tomcat_bec/devices/aerotech/AerotechAutomation1.py +++ b/tomcat_bec/devices/aerotech/AerotechAutomation1.py @@ -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) diff --git a/tomcat_bec/devices/gigafrost/gigafrostclient.py b/tomcat_bec/devices/gigafrost/gigafrostclient.py index 3cb4623..a10a011 100644 --- a/tomcat_bec/devices/gigafrost/gigafrostclient.py +++ b/tomcat_bec/devices/gigafrost/gigafrostclient.py @@ -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. # """ diff --git a/tomcat_bec/devices/gigafrost/stddaq_client.py b/tomcat_bec/devices/gigafrost/stddaq_client.py index 64b6ffe..3209d6f 100644 --- a/tomcat_bec/devices/gigafrost/stddaq_client.py +++ b/tomcat_bec/devices/gigafrost/stddaq_client.py @@ -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 diff --git a/tomcat_bec/devices/gigafrost/stddaq_preview.py b/tomcat_bec/devices/gigafrost/stddaq_preview.py index 3742a01..051b53b 100644 --- a/tomcat_bec/devices/gigafrost/stddaq_preview.py +++ b/tomcat_bec/devices/gigafrost/stddaq_preview.py @@ -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__": diff --git a/tomcat_bec/scans/tomcat_scanbase.py b/tomcat_bec/scans/tomcat_scanbase.py index f604377..61b261a 100644 --- a/tomcat_bec/scans/tomcat_scanbase.py +++ b/tomcat_bec/scans/tomcat_scanbase.py @@ -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() diff --git a/tomcat_bec/scripts/demoscans.py b/tomcat_bec/scripts/demoscans.py index f22f954..333b5ef 100644 --- a/tomcat_bec/scripts/demoscans.py +++ b/tomcat_bec/scripts/demoscans.py @@ -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()