diff --git a/tomcat_bec/device_configs/microxas_test_bed.yaml b/tomcat_bec/device_configs/microxas_test_bed.yaml index 7edb0a3..b656f3a 100644 --- a/tomcat_bec/device_configs/microxas_test_bed.yaml +++ b/tomcat_bec/device_configs/microxas_test_bed.yaml @@ -49,9 +49,9 @@ femto_mean_curr: softwareTrigger: false es1_roty: - readoutPriority: baseline + readoutPriority: monitored description: 'Test rotation stage' - deviceClass: tomcat_bec.devices.psimotor.EpicsMotorMR + deviceClass: ophyd.EpicsMotor deviceConfig: prefix: X02DA-ES1-SMP1:ROTY deviceTags: @@ -61,59 +61,59 @@ es1_roty: readOnly: false softwareTrigger: false -es1_ismc: - description: 'Automation1 iSMC interface' - deviceClass: tomcat_bec.devices.aa1Controller - deviceConfig: - prefix: 'X02DA-ES1-SMP1:CTRL:' - deviceTags: - - es1 - enabled: true - onFailure: buffer - readOnly: false - readoutPriority: monitored - softwareTrigger: false +# es1_ismc: +# description: 'Automation1 iSMC interface' +# deviceClass: tomcat_bec.devices.aa1Controller +# deviceConfig: +# prefix: 'X02DA-ES1-SMP1:CTRL:' +# deviceTags: +# - es1 +# enabled: true +# onFailure: buffer +# readOnly: false +# readoutPriority: monitored +# softwareTrigger: false -es1_tasks: - description: 'Automation1 task management interface' - deviceClass: tomcat_bec.devices.aa1Tasks - deviceConfig: - prefix: 'X02DA-ES1-SMP1:TASK:' - deviceTags: - - es1 - enabled: true - onFailure: buffer - readOnly: false - readoutPriority: monitored - softwareTrigger: false +# es1_tasks: +# description: 'Automation1 task management interface' +# deviceClass: tomcat_bec.devices.aa1Tasks +# deviceConfig: +# prefix: 'X02DA-ES1-SMP1:TASK:' +# deviceTags: +# - es1 +# enabled: true +# onFailure: buffer +# readOnly: false +# readoutPriority: monitored +# softwareTrigger: false -es1_psod: - description: 'AA1 PSO output interface (trigger)' - deviceClass: tomcat_bec.devices.aa1AxisPsoDistance - deviceConfig: - prefix: 'X02DA-ES1-SMP1:ROTY:PSO:' - deviceTags: - - es1 - enabled: true - onFailure: buffer - readOnly: false - readoutPriority: monitored - softwareTrigger: true +# es1_psod: +# description: 'AA1 PSO output interface (trigger)' +# deviceClass: tomcat_bec.devices.aa1AxisPsoDistance +# deviceConfig: +# prefix: 'X02DA-ES1-SMP1:ROTY:PSO:' +# deviceTags: +# - es1 +# enabled: true +# onFailure: buffer +# readOnly: false +# readoutPriority: monitored +# softwareTrigger: true -es1_ddaq: - description: 'Automation1 position recording interface' - deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection - deviceConfig: - prefix: 'X02DA-ES1-SMP1:ROTY:DDC:' - deviceTags: - - es1 - enabled: true - onFailure: buffer - readOnly: false - readoutPriority: monitored - softwareTrigger: false +# es1_ddaq: +# description: 'Automation1 position recording interface' +# deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection +# deviceConfig: +# prefix: 'X02DA-ES1-SMP1:ROTY:DDC:' +# deviceTags: +# - es1 +# enabled: true +# onFailure: buffer +# readOnly: false +# readoutPriority: monitored +# softwareTrigger: false #camera: diff --git a/tomcat_bec/device_configs/microxas_test_bed_tmp.yaml b/tomcat_bec/device_configs/microxas_test_bed_tmp.yaml new file mode 100644 index 0000000..7edb0a3 --- /dev/null +++ b/tomcat_bec/device_configs/microxas_test_bed_tmp.yaml @@ -0,0 +1,186 @@ +eyex: + readoutPriority: baseline + description: X-ray eye axis X + deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC + deviceConfig: + prefix: MTEST-X05LA-ES2-XRAYEYE:M1 + deviceTags: + - xray-eye + onFailure: buffer + enabled: true + readOnly: false + softwareTrigger: false +# eyey: +# readoutPriority: baseline +# description: X-ray eye axis Y +# deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC +# deviceConfig: +# prefix: MTEST-X05LA-ES2-XRAYEYE:M2 +# deviceTags: +# - xray-eye +# onFailure: buffer +# enabled: true +# readOnly: false +# softwareTrigger: false +# eyez: +# readoutPriority: baseline +# description: X-ray eye axis Z +# deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC +# deviceConfig: +# prefix: MTEST-X05LA-ES2-XRAYEYE:M3 +# deviceTags: +# - xray-eye +# onFailure: buffer +# enabled: true +# readOnly: false +# softwareTrigger: false +femto_mean_curr: + readoutPriority: monitored + description: Femto mean current + deviceClass: ophyd.EpicsSignal + deviceConfig: + auto_monitor: true + read_pv: MTEST-X05LA-ES2-XRAYEYE:FEMTO-MEAN-CURR + deviceTags: + - xray-eye + onFailure: buffer + enabled: true + readOnly: true + softwareTrigger: false + +es1_roty: + readoutPriority: baseline + description: 'Test rotation stage' + deviceClass: tomcat_bec.devices.psimotor.EpicsMotorMR + deviceConfig: + prefix: X02DA-ES1-SMP1:ROTY + deviceTags: + - es1-sam + onFailure: buffer + enabled: true + readOnly: false + softwareTrigger: false + +es1_ismc: + description: 'Automation1 iSMC interface' + deviceClass: tomcat_bec.devices.aa1Controller + deviceConfig: + prefix: 'X02DA-ES1-SMP1:CTRL:' + deviceTags: + - es1 + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: monitored + softwareTrigger: false + +es1_tasks: + description: 'Automation1 task management interface' + deviceClass: tomcat_bec.devices.aa1Tasks + deviceConfig: + prefix: 'X02DA-ES1-SMP1:TASK:' + deviceTags: + - es1 + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: monitored + softwareTrigger: false + + +es1_psod: + description: 'AA1 PSO output interface (trigger)' + deviceClass: tomcat_bec.devices.aa1AxisPsoDistance + deviceConfig: + prefix: 'X02DA-ES1-SMP1:ROTY:PSO:' + deviceTags: + - es1 + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: monitored + softwareTrigger: true + + +es1_ddaq: + description: 'Automation1 position recording interface' + deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection + deviceConfig: + prefix: 'X02DA-ES1-SMP1:ROTY:DDC:' + deviceTags: + - es1 + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: monitored + softwareTrigger: false + + +#camera: +# description: Grashopper Camera +# deviceClass: tomcat_bec.devices.GrashopperTOMCAT +# deviceConfig: +# prefix: 'X02DA-PG-USB:' +# deviceTags: +# - camera +# enabled: true +# onFailure: buffer +# readOnly: false +# readoutPriority: monitored +# softwareTrigger: true + +gfcam: + description: GigaFrost camera client + deviceClass: tomcat_bec.devices.GigaFrostCamera + deviceConfig: + prefix: 'X02DA-CAM-GF2:' + backend_url: 'http://sls-daq-001:8080' + auto_soft_enable: true + deviceTags: + - camera + - trigger + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: monitored + softwareTrigger: true + +gfdaq: + description: GigaFrost stdDAQ client + deviceClass: tomcat_bec.devices.StdDaqClient + deviceConfig: + ws_url: 'ws://129.129.95.111:8080' + rest_url: 'http://129.129.95.111:5000' + deviceTags: + - std-daq + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: monitored + softwareTrigger: false + +daq_stream0: + description: stdDAQ preview (2 every 555) + deviceClass: tomcat_bec.devices.StdDaqPreviewDetector + deviceConfig: + url: 'tcp://129.129.95.111:20000' + deviceTags: + - std-daq + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: monitored + softwareTrigger: false + +daq_stream1: + description: stdDAQ preview (1 at 5 Hz) + deviceClass: tomcat_bec.devices.StdDaqPreviewDetector + deviceConfig: + url: 'tcp://129.129.95.111:20001' + deviceTags: + - std-daq + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: monitored + softwareTrigger: false diff --git a/tomcat_bec/devices/gigafrost/gigafrostcamera.py b/tomcat_bec/devices/gigafrost/gigafrostcamera.py index b60c603..5d1181f 100644 --- a/tomcat_bec/devices/gigafrost/gigafrostcamera.py +++ b/tomcat_bec/devices/gigafrost/gigafrostcamera.py @@ -256,7 +256,7 @@ class GigaFrostCamera(PSIDetectorBase): # pylint: disable=too-many-instance-attributes custom_prepare_cls = GigaFrostCameraMixin - USER_ACCESS = [""] + USER_ACCESS = ["initialize"] _initialized = False infoBusyFlag = Component(EpicsSignalRO, "BUSY_STAT", auto_monitor=True) @@ -466,7 +466,7 @@ class GigaFrostCamera(PSIDetectorBase): def initialize(self): """ Initialization in separate command""" - self.custom_prepare_cls._init_gigafrost() + self.custom_prepare._init_gigafrost() self._initialized = True def trigger(self) -> DeviceStatus: diff --git a/tomcat_bec/devices/gigafrost/stddaq_client.py b/tomcat_bec/devices/gigafrost/stddaq_client.py index f2e87d6..3bc0e11 100644 --- a/tomcat_bec/devices/gigafrost/stddaq_client.py +++ b/tomcat_bec/devices/gigafrost/stddaq_client.py @@ -56,10 +56,10 @@ class StdDaqMixin(CustomDeviceMixin): if "steps" in scanargs and scanargs['steps'] is not None: num_points *= scanargs["steps"] points_valid = True - elif "exp_burst" in scanargs and scanargs['exp_burst'] is not None: + if "exp_burst" in scanargs and scanargs['exp_burst'] is not None: num_points *= scanargs["exp_burst"] points_valid = True - elif "repeats" in scanargs and scanargs['repeats'] is not None: + if "repeats" in scanargs and scanargs['repeats'] is not None: num_points *= scanargs["repeats"] points_valid = True if points_valid: @@ -67,19 +67,16 @@ class StdDaqMixin(CustomDeviceMixin): # Perform bluesky-style configuration if len(d) > 0: - print('Reconfiguring') - # Stop if current status is not idle - if self.parent.state() != "idle": - self.parent.blueunstage() - # Configure new run (will restart the stdDAQ) - logger.warning(f"[{self.parent.name}] Configuring with:\n{d}") + logger.warning(f"[{self.parent.name}] stdDAQ needs reconfiguring with:\n{d}") self.parent.configure(d=d) + # Wait for REST API to kill the DAQ + sleep(0.5) - # Try to start a new run + # Try to start a new run (reconnects) self.parent.bluestage() # And start status monitoring - self._mon = Thread(target=self.poll, daemon=True) + self._mon = Thread(target=self.monitor, daemon=True) self._mon.start() def on_unstage(self): @@ -93,7 +90,7 @@ class StdDaqMixin(CustomDeviceMixin): """ self.parent.blueunstage() - def poll(self) -> None: + def monitor(self) -> None: """ Monitor status messages while connection is open. This will block the reply monitoring to calling unstage() might throw. Status updates are sent every 1 seconds, but finishing acquisition means StdDAQ will close connection, so there's no idle state polling. @@ -269,8 +266,6 @@ class StdDaqClient(PSIDeviceBase): * 4: Invert pixel values, but do not apply any linearity correction * 5: Apply the full linearity correction """ - print('This is d') - print(d) if 'image_width' in d and d['image_width']!=None: self.cfg_pixel_width.set(d['image_width']).wait() if 'image_height' in d and d['image_height']!=None: @@ -283,9 +278,6 @@ class StdDaqClient(PSIDeviceBase): # Restart the DAQ if resolution changed cfg = self.get_daq_config() - print(cfg) - print(cfg['number_of_writers']) - print(self.cfg_nr_writers.get()) if cfg['image_pixel_height'] != self.cfg_pixel_height.get() or \ cfg['image_pixel_width'] != self.cfg_pixel_width.get() or \ cfg['bit_depth'] != self.cfg_bit_depth.get() or \ @@ -293,29 +285,22 @@ class StdDaqClient(PSIDeviceBase): # Stop if current status is not idle status = self.state() - print(status) + if self.state() != "idle": + raise RuntimeWarning(f"[{self.name}] stdDAQ reconfiguration might corrupt files") + # Stop running acquisition + try: + self.stop() + except RuntimeError: + raise RuntimeWarning(f"[{self.name}] Told ya!") -# if self.state() != "idle": - # if status != "idle": -# self.surestop() - - status = self.state() - print(status) - - # Stop running acquisition - print('Before unstage') - self.unstage() - print('After unstage') # Update retrieved config cfg['image_pixel_height'] = int(self.cfg_pixel_height.get()) cfg['image_pixel_width'] = int(self.cfg_pixel_width.get()) cfg['bit_depth'] = int(self.cfg_bit_depth.get()) cfg['number_of_writers'] = int(self.cfg_nr_writers.get()) - print(cfg) r = self.set_daq_config(cfg) - print(r) cfg=self.read_daq_config() - print(cfg) + def bluestage(self): """ Stages the stdDAQ @@ -324,34 +309,37 @@ class StdDaqClient(PSIDeviceBase): the current configuration. It waits for the first reply and checks it for obvious failures. """ + # Can't stage into a running exposure + if self.state() != 'idle': + raise RuntimeError(f"[{self.name}] stdDAQ can't stage from state: {self.state()}") + file_path = self.file_path.get() num_images = self.num_images.get() + + # New connection + self.connect() message = {"command": "start", "path": file_path, "n_image": num_images, } + reply = self.message(message) - ii = 0 - while ii<5: - self.connect() - reply = self.message(message) + if reply is not None: + reply = json.loads(reply) + self.status.set(reply["status"], force=True).wait() + logger.info(f"[{self.name}] Start DAQ reply: {reply}") - if reply is not None: - reply = json.loads(reply) - self.status.set(reply["status"], force=True).wait() - logger.info(f"[{self.name}] Start DAQ reply: {reply}") - - # Give it more time to reconfigure - if reply["status"] in ("rejected"): - # FIXME: running exposure is a nogo - if reply['reason'] == "driver is busy!": - raise RuntimeError(f"[{self.name}] Start stdDAQ command rejected: already running") - else: - # Give it more time to consolidate - sleep(1) + # Give it more time to reconfigure + if reply["status"] in ("rejected"): + # FIXME: running exposure is a nogo + if reply['reason'] == "driver is busy!": + raise RuntimeError(f"[{self.name}] Start stdDAQ command rejected: already running") else: - # Success!!! - print(f"[{self.name}] Started stdDAQ on try {ii} in: {reply['status']}") - return - ii += 1 - raise RuntimeError(f"[{self.name}] Failed to start the stdDAQ in 5 tries, reason: {reply['reason']}") + # Give it more time to consolidate + sleep(1) + else: + # Success!!! + print(f"[{self.name}] Started stdDAQ in: {reply['status']}") + return + + raise RuntimeError(f"[{self.name}] Failed to start the stdDAQ in 1 tries, reason: {reply['reason']}") def blueunstage(self): """ Unstages the stdDAQ @@ -439,11 +427,14 @@ class StdDaqClient(PSIDeviceBase): headers = {'Content-type': 'application/json'} ) - def nuke(self): - """ Reconfigures the stdDAQ to restart the services + def nuke(self, restarttime=5): + """ Reconfigures the stdDAQ to restart the services. This causes + systemd to kill the current DAQ service and restart it with the same + configuration. Which might corrupt the currently written file... """ cfg = self.get_daq_config() self.set_daq_config(cfg) + sleep(restarttime) def state(self) -> str | None: """ Querry the current system state""" @@ -458,45 +449,6 @@ class StdDaqClient(PSIDeviceBase): except ConnectionRefusedError: raise - def surestop(self, timeout=5): - """ Stops a running acquisition - - REST reconfiguration restarts with systemd and can corrupt currently written files. - """ - # Retries to steal connection from poller - for rr in range(5): - client = connect(self.ws_url.get()) - msg = json.dumps({"command": "stop_all"}) - client.send(msg) - reply = client.recv(timeout=1) - reply = json.loads(reply) - # logger.warning(reply) - # if r['status'] == 'success': - # break - - sleep(0.5) - - client = connect(self.ws_url.get()) - msg = json.dumps({"command": "status"}) - client.send(msg) - reply = client.recv(timeout=1) - reply = json.loads(reply) - - print(reply['status']) - if reply['status'] in ['idle', 'stoped']: - logger.warning(f"[{self.name}] Stop-all command finished in {reply['status']}") - return - - # If stop_all didn't stop, nuke the whole thing - logger.error(f"[{self.name}] Don't stop, make it rock!!!") - self.nuke() - sleep(timeout) - - - - - - # Automatically connect to microXAS testbench if directly invoked if __name__ == "__main__": diff --git a/tomcat_bec/devices/gigafrost/stddaq_preview.py b/tomcat_bec/devices/gigafrost/stddaq_preview.py index 8ee712d..3a08126 100644 --- a/tomcat_bec/devices/gigafrost/stddaq_preview.py +++ b/tomcat_bec/devices/gigafrost/stddaq_preview.py @@ -102,6 +102,7 @@ class StdDaqPreviewMixin(CustomDetectorMixin): self.parent.frame.put(header['frame'], force=True) self.parent.image_shape.put(header['shape'], force=True) self.parent.image.put(image, force=True) + self.parent._last_image = image self.parent._run_subs(sub_type=self.parent.SUB_MONITOR, value=image) t_last = t_curr logger.info( @@ -134,7 +135,7 @@ class StdDaqPreviewDetector(PSIDetectorBase): cam_widget = gui.add_dock('cam_dock1').add_widget('BECFigure').image('daq_stream1') """ # Subscriptions for plotting image - USER_ACCESS = ["kickoff"] + USER_ACCESS = ["kickoff", "get_last_image"] SUB_MONITOR = "device_monitor_2d" _default_sub = SUB_MONITOR @@ -147,7 +148,8 @@ class StdDaqPreviewDetector(PSIDetectorBase): frame = Component(Signal, kind=Kind.hinted) image_shape = Component(Signal, kind=Kind.normal) # FIXME: The BEC client caches the read()s from the last 50 scans - image = Component(Signal, kind=Kind.config) + image = Component(Signal, kind=Kind.omitted) + _last_image = None def __init__( self, *args, url: str = "tcp://129.129.95.38:20000", parent: Device = None, **kwargs @@ -180,6 +182,9 @@ class StdDaqPreviewDetector(PSIDetectorBase): sleep(1) self._socket.connect(self.url.get()) + def get_image(self): + return self._last_image + def kickoff(self) -> DeviceStatus: """ The DAQ was not meant to be toggled""" return DeviceStatus(self, done=True, success=True, settle_time=0.1) diff --git a/tomcat_bec/scripts/anotherroundsans.py b/tomcat_bec/scripts/anotherroundsans.py index f90fdea..d8d9247 100644 --- a/tomcat_bec/scripts/anotherroundsans.py +++ b/tomcat_bec/scripts/anotherroundsans.py @@ -14,8 +14,8 @@ def dev_disable_all(): """Disable all devices """ - for d in dev: - d.enabled = False + for k in dev: + dev[k].enabled = False @@ -41,12 +41,12 @@ def anotherstepscan( -------- demostepscan(scan_start=-32, scan_end=148, steps=180, exp_time=0.005, exp_burst=5) """ - if not bl_check_beam(): - raise RuntimeError("Beamline is not in ready state") - - dev.es1_tasks.enabled = False - dev.es1_psod.enabled = False - dev.es1_ddaq.enabled = True + # if not bl_check_beam(): + # raise RuntimeError("Beamline is not in ready state") + + dev_disable_all() + dev.es1_roty.enabled = True + #dev.es1_ddaq.enabled = True dev.gfcam.enabled = True dev.gfdaq.enabled = True dev.daq_stream0.enabled = True