From 0bded12b4e2be0a78516fd87ad6204e35b6c78e7 Mon Sep 17 00:00:00 2001 From: gac-x06da Date: Wed, 12 Mar 2025 14:21:30 +0100 Subject: [PATCH 01/11] Added AttributeError to updates --- pxiii_bec/bec_widgets/auto_updates.py | 2 + .../device_configs/x06da_device_config.yaml | 37 ++++++++++++++++++- pxiii_bec/scripts/beamlinescripts.py | 4 +- pxiii_bec/scripts/scanwrappers.py | 4 +- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/pxiii_bec/bec_widgets/auto_updates.py b/pxiii_bec/bec_widgets/auto_updates.py index 543fde8..89cb7ff 100644 --- a/pxiii_bec/bec_widgets/auto_updates.py +++ b/pxiii_bec/bec_widgets/auto_updates.py @@ -56,6 +56,8 @@ class PlotUpdate(AutoUpdates): plt1.set(title=f"PXIII: Scan {info.scan_number}", x_label=dev_x, y_label=dev_y) except RPCResponseTimeoutError: pass + except AttributeError: + pass # plt1.add_dap(dev_x, dev_y, dap="LinearModel") def handler(self, info: ScanInfo) -> None: diff --git a/pxiii_bec/device_configs/x06da_device_config.yaml b/pxiii_bec/device_configs/x06da_device_config.yaml index 818b7d6..c5fda49 100644 --- a/pxiii_bec/device_configs/x06da_device_config.yaml +++ b/pxiii_bec/device_configs/x06da_device_config.yaml @@ -1,3 +1,36 @@ +sls_current: + description: SLS current + deviceClass: ophyd.EpicsSignalRO + deviceConfig: {read_pv: 'ARS07-DPCT-0100:CURR', auto_monitor: true} + onFailure: buffer + enabled: true + readoutPriority: monitored + readOnly: true + softwareTrigger: false +vg0_press: + description: VG0 pressure + deviceClass: ophyd.EpicsSignalRO + deviceConfig: {read_pv: 'X06DA-FE-VMCC-0000:PRESSURE', auto_monitor: true} + onFailure: buffer + enabled: true + readoutPriority: monitored + readOnly: true + softwareTrigger: false +abs_press: + description: Absorber pressure + deviceClass: ophyd.EpicsSignalRO + deviceConfig: {read_pv: 'X06DA-FE-ABS1-VMCC-1010:PRESSURE', auto_monitor: true} + onFailure: buffer + enabled: true + readoutPriority: monitored + readOnly: true + softwareTrigger: false + + + + + + sldi_cenx: description: FE slit-diaphragm horizontal center deviceClass: ophyd.EpicsMotor @@ -10,7 +43,7 @@ sldi_cenx: sldi_sizex: description: FE slit-diaphragm horizontal size deviceClass: ophyd.EpicsMotor - deviceConfig: {prefix: 'X06DA-FE-SLDI:GAPX'} + deviceConfig: {prefix: 'X06DA-FE-SLDI:SIZEX'} onFailure: buffer enabled: true readoutPriority: monitored @@ -28,7 +61,7 @@ sldi_ceny: sldi_sizey: description: FE slit-diaphragm vertical size deviceClass: ophyd.EpicsMotor - deviceConfig: {prefix: 'X06DA-FE-SLDI:GAPY'} + deviceConfig: {prefix: 'X06DA-FE-SLDI:SIZEY'} onFailure: buffer enabled: true readoutPriority: monitored diff --git a/pxiii_bec/scripts/beamlinescripts.py b/pxiii_bec/scripts/beamlinescripts.py index 32d92ff..af5dcdc 100644 --- a/pxiii_bec/scripts/beamlinescripts.py +++ b/pxiii_bec/scripts/beamlinescripts.py @@ -1,5 +1,5 @@ -import bec -import bec_lib.devicemanager.DeviceContainer as dev +# import bec +# import bec_lib.devicemanager.DeviceContainer as dev def rock(steps, exp_time, scan_start=None, scan_end=None, datasource=None, visual=True, **kwargs): diff --git a/pxiii_bec/scripts/scanwrappers.py b/pxiii_bec/scripts/scanwrappers.py index a42c5cf..51e5713 100644 --- a/pxiii_bec/scripts/scanwrappers.py +++ b/pxiii_bec/scripts/scanwrappers.py @@ -1,5 +1,5 @@ -import bec -import bec_lib.devicemanager.DeviceContainer as dev +# import bec +# import bec_lib.devicemanager.DeviceContainer as dev def bl_check_beam(): -- 2.49.1 From 6b80009bec502b49e369ea36d10c82fc1680e538 Mon Sep 17 00:00:00 2001 From: gac-x06da Date: Fri, 14 Mar 2025 12:32:12 +0100 Subject: [PATCH 02/11] Keyword scans for 2D --- pxiii_bec/bec_widgets/auto_updates.py | 66 +++++++++++++------ .../device_configs/x06da_device_config.yaml | 4 -- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/pxiii_bec/bec_widgets/auto_updates.py b/pxiii_bec/bec_widgets/auto_updates.py index 89cb7ff..5782267 100644 --- a/pxiii_bec/bec_widgets/auto_updates.py +++ b/pxiii_bec/bec_widgets/auto_updates.py @@ -24,41 +24,68 @@ class PlotUpdate(AutoUpdates): # plt = self.figure.plot(dev_x, dev_y) # plt.set(title=f"PXIII: Scan {info.scan_number}", x_label=dev_x, y_label=dev_y) - def keyword_handler(self, info: ScanInfo) -> None: - """Simple keyword handler + def plot_handler(self, info: ScanInfo) -> None: + """Simple keyword handler for 'plot' - This simple keyword handler looks for the keyword 'datasource' in the scan arguments. + This simple keyword handler looks for the keyword 'plot' in the scan arguments. This allows the user to explictly specify the desired data source. Useful for alignment scans. """ + print(info.scan_report_devices) + dev_x = info.scan_report_devices[0] if "kwargs" in self._scan_msg.info: - dev_y = self._scan_msg.info["kwargs"].get("datasource", None) + det = self._scan_msg.info["kwargs"].get("plot", None) else: - dev_y = None - if not dev_y: + det = None + if not det: return plt1 = self.get_default_figure() + # clear_all() will throw RPCResponseTimeoutError try: - # This will throw RPCResponseTimeoutError plt1.clear_all() except RPCResponseTimeoutError: pass + + # plot() will throw RPCResponseTimeoutError try: - # TODO: What about 2D scans? - # This will throw RPCResponseTimeoutError - plt1.plot(x_name=dev_x, y_name=dev_y) + if len(info.scan_report_devices)==2: + # 2D plot + dev_x = info.scan_report_devices[0] + dev_y = info.scan_report_devices[1] + plt1.plot( + x_name=dev_x, + y_name=dev_y, + z_name=det, + title=f"Scan {info.scan_number}", + x_label=dev_x, + y_label=dev_y, + z_label=det + ) + elif len(info.scan_report_devices)==1: + # 1D plot + dev_x = info.scan_report_devices[0] + plt1.plot( + x_name=dev_x, + y_name=det, + title=f"Scan {info.scan_number}", + x_label=dev_x, + y_label=det, + ) + else: + # Default is 1D + dev_x = info.scan_report_devices[0] + plt1.plot( + x_name=dev_x, + y_name=det, + title=f"Scan {info.scan_number}", + x_label=dev_x, + y_label=det, + ) except RPCResponseTimeoutError: pass - try: - # This will throw RPCResponseTimeoutError - plt1.set(title=f"PXIII: Scan {info.scan_number}", x_label=dev_x, y_label=dev_y) - except RPCResponseTimeoutError: - pass - except AttributeError: - pass - # plt1.add_dap(dev_x, dev_y, dap="LinearModel") + # plt1.add_dap(dev_x, det, dap="LinearModel") def handler(self, info: ScanInfo) -> None: """Dock configuration handler""" @@ -70,4 +97,5 @@ class PlotUpdate(AutoUpdates): # self.run_grid_scan_update(info) # return super().handler(info) - self.keyword_handler(info) + self.plot_handler(info) + diff --git a/pxiii_bec/device_configs/x06da_device_config.yaml b/pxiii_bec/device_configs/x06da_device_config.yaml index c5fda49..f9f30e4 100644 --- a/pxiii_bec/device_configs/x06da_device_config.yaml +++ b/pxiii_bec/device_configs/x06da_device_config.yaml @@ -27,10 +27,6 @@ abs_press: softwareTrigger: false - - - - sldi_cenx: description: FE slit-diaphragm horizontal center deviceClass: ophyd.EpicsMotor -- 2.49.1 From 689089fbbbec19a7a3d4180625240b0faf5c58cc Mon Sep 17 00:00:00 2001 From: gac-x06da Date: Fri, 14 Mar 2025 12:39:42 +0100 Subject: [PATCH 03/11] Blacking --- pxiii_bec/bec_widgets/auto_updates.py | 9 ++++----- pxiii_bec/scripts/beamlinescripts.py | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pxiii_bec/bec_widgets/auto_updates.py b/pxiii_bec/bec_widgets/auto_updates.py index 5782267..75dec70 100644 --- a/pxiii_bec/bec_widgets/auto_updates.py +++ b/pxiii_bec/bec_widgets/auto_updates.py @@ -50,7 +50,7 @@ class PlotUpdate(AutoUpdates): # plot() will throw RPCResponseTimeoutError try: - if len(info.scan_report_devices)==2: + if len(info.scan_report_devices) == 2: # 2D plot dev_x = info.scan_report_devices[0] dev_y = info.scan_report_devices[1] @@ -61,9 +61,9 @@ class PlotUpdate(AutoUpdates): title=f"Scan {info.scan_number}", x_label=dev_x, y_label=dev_y, - z_label=det + z_label=det, ) - elif len(info.scan_report_devices)==1: + elif len(info.scan_report_devices) == 1: # 1D plot dev_x = info.scan_report_devices[0] plt1.plot( @@ -97,5 +97,4 @@ class PlotUpdate(AutoUpdates): # self.run_grid_scan_update(info) # return super().handler(info) - self.plot_handler(info) - + self.plot_handler(info) \ No newline at end of file diff --git a/pxiii_bec/scripts/beamlinescripts.py b/pxiii_bec/scripts/beamlinescripts.py index af5dcdc..b4511fb 100644 --- a/pxiii_bec/scripts/beamlinescripts.py +++ b/pxiii_bec/scripts/beamlinescripts.py @@ -1,3 +1,4 @@ +# pylint: disable=undefined-variable # import bec # import bec_lib.devicemanager.DeviceContainer as dev -- 2.49.1 From 2cad486156b4cc7e5e0d344010ab88899888f9e6 Mon Sep 17 00:00:00 2001 From: gac-x06da Date: Fri, 14 Mar 2025 16:10:16 +0100 Subject: [PATCH 04/11] WIP --- pxiii_bec/bec_widgets/auto_updates.py | 2 +- pxiii_bec/device_configs/x06da_device_config.yaml | 2 +- pxiii_bec/scripts/scanwrappers.py | 12 ++++++------ pyproject.toml | 1 + 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pxiii_bec/bec_widgets/auto_updates.py b/pxiii_bec/bec_widgets/auto_updates.py index 75dec70..ccecd00 100644 --- a/pxiii_bec/bec_widgets/auto_updates.py +++ b/pxiii_bec/bec_widgets/auto_updates.py @@ -97,4 +97,4 @@ class PlotUpdate(AutoUpdates): # self.run_grid_scan_update(info) # return super().handler(info) - self.plot_handler(info) \ No newline at end of file + self.plot_handler(info) diff --git a/pxiii_bec/device_configs/x06da_device_config.yaml b/pxiii_bec/device_configs/x06da_device_config.yaml index f9f30e4..1b4008c 100644 --- a/pxiii_bec/device_configs/x06da_device_config.yaml +++ b/pxiii_bec/device_configs/x06da_device_config.yaml @@ -47,7 +47,7 @@ sldi_sizex: softwareTrigger: false sldi_ceny: description: FE slit-diaphragm vertical center - deviceClass: ophyd.EpicsMotor + deviceClass: ophyd_devices.EpicsMotorEC deviceConfig: {prefix: 'X06DA-FE-SLDI:CENY'} onFailure: buffer enabled: true diff --git a/pxiii_bec/scripts/scanwrappers.py b/pxiii_bec/scripts/scanwrappers.py index 51e5713..82dd10a 100644 --- a/pxiii_bec/scripts/scanwrappers.py +++ b/pxiii_bec/scripts/scanwrappers.py @@ -13,7 +13,7 @@ def ascan( scan_end, steps, exp_time, - datasource=None, + plot=None, visual=True, relative=False, **kwargs, @@ -44,10 +44,10 @@ def ascan( # Draw a simploe plot in the window dock = window.add_dock(f"ScanDisplay {motor}") plt1 = dock.add_widget("BECWaveformWidget") - plt1.plot(x_name=motor, y_name=datasource) + plt1.plot(x_name=motor, y_name=plot) plt1.set_x_label(motor) - plt1.set_y_label(datasource) - plt1.add_dap(motor, datasource, dap="LinearModel") + plt1.set_y_label(plot) + plt1.add_dap(motor, plot, dap="LinearModel") window.show() print("Handing over to 'scans.line_scan'") @@ -57,7 +57,7 @@ def ascan( scan_end, steps=steps, exp_time=exp_time, - datasource=datasource, + plot=plot, relative=relative, **kwargs, ) @@ -68,7 +68,7 @@ def ascan( else: # Fitting without GUI firt_par = bec.dap.LinearModel.fit( - s, motor.name, motor.name, datasource.name, datasource.name + s, motor.name, motor.name, plot.name, plot.name ) # # Some basic fit diff --git a/pyproject.toml b/pyproject.toml index f030919..f3196ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "bec_ipython_client", "bec_lib", "bec_server", + "bec_widgets", "ophyd_devices", "std_daq_client", "rich", -- 2.49.1 From 588316262c7d2d3d05111e72038e76565b6c3bd7 Mon Sep 17 00:00:00 2001 From: gac-x06da Date: Fri, 21 Mar 2025 09:27:29 +0100 Subject: [PATCH 05/11] WIP --- pxiii_bec/device_configs/x06da_device_config.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pxiii_bec/device_configs/x06da_device_config.yaml b/pxiii_bec/device_configs/x06da_device_config.yaml index 1b4008c..c5e4f64 100644 --- a/pxiii_bec/device_configs/x06da_device_config.yaml +++ b/pxiii_bec/device_configs/x06da_device_config.yaml @@ -5,6 +5,9 @@ sls_current: onFailure: buffer enabled: true readoutPriority: monitored + deviceTags: + - ring + - fe readOnly: true softwareTrigger: false vg0_press: @@ -14,6 +17,8 @@ vg0_press: onFailure: buffer enabled: true readoutPriority: monitored + deviceTags: + - fe readOnly: true softwareTrigger: false abs_press: @@ -23,6 +28,8 @@ abs_press: onFailure: buffer enabled: true readoutPriority: monitored + deviceTags: + - fe readOnly: true softwareTrigger: false @@ -34,6 +41,8 @@ sldi_cenx: onFailure: buffer enabled: true readoutPriority: monitored + deviceTags: + - fe readOnly: false softwareTrigger: false sldi_sizex: @@ -43,6 +52,8 @@ sldi_sizex: onFailure: buffer enabled: true readoutPriority: monitored + deviceTags: + - fe readOnly: false softwareTrigger: false sldi_ceny: @@ -52,6 +63,8 @@ sldi_ceny: onFailure: buffer enabled: true readoutPriority: monitored + deviceTags: + - fe readOnly: false softwareTrigger: false sldi_sizey: @@ -61,6 +74,8 @@ sldi_sizey: onFailure: buffer enabled: true readoutPriority: monitored + deviceTags: + - fe readOnly: false softwareTrigger: false -- 2.49.1 From 43c84237a9aa3cbaaa6a7fb2d4d5ecd8e533b989 Mon Sep 17 00:00:00 2001 From: Unknown MX Person Date: Tue, 25 Mar 2025 15:10:33 +0100 Subject: [PATCH 06/11] changed config file --- .../device_configs/x06da_device_config.yaml | 102 +++++++++++++----- pxiii_bec/scripts/beamlinescripts.py | 9 ++ 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/pxiii_bec/device_configs/x06da_device_config.yaml b/pxiii_bec/device_configs/x06da_device_config.yaml index c5e4f64..0f61b68 100644 --- a/pxiii_bec/device_configs/x06da_device_config.yaml +++ b/pxiii_bec/device_configs/x06da_device_config.yaml @@ -1,12 +1,11 @@ sls_current: - description: SLS current + description: sls current deviceClass: ophyd.EpicsSignalRO deviceConfig: {read_pv: 'ARS07-DPCT-0100:CURR', auto_monitor: true} onFailure: buffer enabled: true readoutPriority: monitored deviceTags: - - ring - fe readOnly: true softwareTrigger: false @@ -37,7 +36,7 @@ abs_press: sldi_cenx: description: FE slit-diaphragm horizontal center deviceClass: ophyd.EpicsMotor - deviceConfig: {prefix: 'X06DA-FE-SLDI:CENX'} + deviceConfig: {prefix: 'X06DA-FE-SLDI:CENTERX'} onFailure: buffer enabled: true readoutPriority: monitored @@ -59,7 +58,7 @@ sldi_sizex: sldi_ceny: description: FE slit-diaphragm vertical center deviceClass: ophyd_devices.EpicsMotorEC - deviceConfig: {prefix: 'X06DA-FE-SLDI:CENY'} + deviceConfig: {prefix: 'X06DA-FE-SLDI:CENTERY'} onFailure: buffer enabled: true readoutPriority: monitored @@ -79,6 +78,50 @@ sldi_sizey: readOnly: false softwareTrigger: false +fecmi_try: + description: FE collimating mirror try + deviceClass: ophyd.EpicsMotor + deviceConfig: {prefix: 'X06DA-FE-MI1:TRY'} + onFailure: buffer + enabled: true + readoutPriority: monitored + deviceTags: + - fe + readOnly: false + softwareTrigger: false +fecmi_pitch: + description: FE collimating mirror pitch + deviceClass: ophyd.EpicsMotor + deviceConfig: {prefix: 'X06DA-FE-MI1:PITCH'} + onFailure: buffer + enabled: true + readoutPriority: monitored + deviceTags: + - fe + readOnly: false + softwareTrigger: false +fecmi_bend: + description: FE collimating mirror bend + deviceClass: ophyd.EpicsMotor + deviceConfig: {prefix: 'X06DA-FE-MI1:BEND1'} + onFailure: buffer + enabled: true + readoutPriority: monitored + deviceTags: + - fe + readOnly: false + softwareTrigger: false + +slh_press: + description: OP slit pressure + deviceClass: ophyd.EpicsSignalRO + deviceConfig: {read_pv: 'X06DA-OP-SLH-VMFR-1010:PRESSURE', auto_monitor: true} + onFailure: buffer + enabled: true + readoutPriority: monitored + readOnly: true + softwareTrigger: false + slh_trxr: description: OP slit inner blade motion deviceClass: ophyd.EpicsMotor @@ -115,8 +158,17 @@ dccm_theta1: readoutPriority: monitored readOnly: false softwareTrigger: false -dccm_diode: - description: Diode between mono crystals +dccm_diode_top: + description: Top diode between mono crystals + deviceClass: ophyd.EpicsSignalRO + deviceConfig: {read_pv: 'X06DA-OP-XPM1:TOP:READOUT', auto_monitor: true} + onFailure: buffer + enabled: true + readoutPriority: monitored + readOnly: true + softwareTrigger: false +dccm_diode_bottom: + description: Bottom diode between mono crystals deviceClass: ophyd.EpicsSignalRO deviceConfig: {read_pv: 'X06DA-OP-XPM1:BOT:READOUT', auto_monitor: true} onFailure: buffer @@ -589,24 +641,7 @@ backlight: readoutPriority: baseline readOnly: false softwareTrigger: false -det_y: - description: Pilatus height - deviceClass: ophyd.EpicsMotor - deviceConfig: {prefix: 'X06DA-ES-DET:TRY1'} - onFailure: buffer - enabled: true - readoutPriority: monitored - readOnly: false - softwareTrigger: false -det_z: - description: Pilatus translation - deviceClass: ophyd.EpicsMotor - deviceConfig: {prefix: 'X06DA-ES-DET:TRZ1'} - onFailure: buffer - enabled: true - readoutPriority: monitored - readOnly: false - softwareTrigger: false + @@ -701,3 +736,22 @@ phi: readoutPriority: monitored readOnly: false softwareTrigger: false + +det_y: + description: Pilatus height + deviceClass: ophyd.EpicsMotor + deviceConfig: {prefix: 'X06DA-ES-DET:TRY1'} + onFailure: buffer + enabled: true + readoutPriority: monitored + readOnly: false + softwareTrigger: false +det_z: + description: Pilatus translation + deviceClass: ophyd.EpicsMotor + deviceConfig: {prefix: 'X06DA-ES-DET:TRZ1'} + onFailure: buffer + enabled: true + readoutPriority: monitored + readOnly: false + softwareTrigger: false \ No newline at end of file diff --git a/pxiii_bec/scripts/beamlinescripts.py b/pxiii_bec/scripts/beamlinescripts.py index b4511fb..95d02dc 100644 --- a/pxiii_bec/scripts/beamlinescripts.py +++ b/pxiii_bec/scripts/beamlinescripts.py @@ -2,6 +2,8 @@ # import bec # import bec_lib.devicemanager.DeviceContainer as dev +import time + def rock(steps, exp_time, scan_start=None, scan_end=None, datasource=None, visual=True, **kwargs): """Demo step scan with plotting @@ -67,3 +69,10 @@ def rock(steps, exp_time, scan_start=None, scan_end=None, datasource=None, visua # TODO: Move to fitted maximum return s, firt_par + + +def monitor(device,steps,t=1): + for _ in range(steps): + print(device.read()) + time.sleep(t) + -- 2.49.1 From c79b919ed61c982c52a2a9a46175dfd117675c8d Mon Sep 17 00:00:00 2001 From: Unknown MX Person Date: Tue, 25 Mar 2025 15:21:02 +0100 Subject: [PATCH 07/11] auto formated --- pxiii_bec/scripts/beamlinescripts.py | 3 +-- pxiii_bec/scripts/scanwrappers.py | 14 ++------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/pxiii_bec/scripts/beamlinescripts.py b/pxiii_bec/scripts/beamlinescripts.py index 95d02dc..8bde700 100644 --- a/pxiii_bec/scripts/beamlinescripts.py +++ b/pxiii_bec/scripts/beamlinescripts.py @@ -71,8 +71,7 @@ def rock(steps, exp_time, scan_start=None, scan_end=None, datasource=None, visua return s, firt_par -def monitor(device,steps,t=1): +def monitor(device, steps, t=1): for _ in range(steps): print(device.read()) time.sleep(t) - diff --git a/pxiii_bec/scripts/scanwrappers.py b/pxiii_bec/scripts/scanwrappers.py index 82dd10a..78a17df 100644 --- a/pxiii_bec/scripts/scanwrappers.py +++ b/pxiii_bec/scripts/scanwrappers.py @@ -8,15 +8,7 @@ def bl_check_beam(): def ascan( - motor, - scan_start, - scan_end, - steps, - exp_time, - plot=None, - visual=True, - relative=False, - **kwargs, + motor, scan_start, scan_end, steps, exp_time, plot=None, visual=True, relative=False, **kwargs ): """Demo step scan with plotting @@ -67,9 +59,7 @@ def ascan( firt_par = plt1.get_dap_params() else: # Fitting without GUI - firt_par = bec.dap.LinearModel.fit( - s, motor.name, motor.name, plot.name, plot.name - ) + firt_par = bec.dap.LinearModel.fit(s, motor.name, motor.name, plot.name, plot.name) # # Some basic fit # dkey = datasource.full_name -- 2.49.1 From 3940cb2da103c96e84e7d838a4a0347d973ca8e0 Mon Sep 17 00:00:00 2001 From: Unknown MX Person Date: Tue, 25 Mar 2025 15:24:46 +0100 Subject: [PATCH 08/11] auto formated --- pxiii_bec/scripts/scanwrappers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pxiii_bec/scripts/scanwrappers.py b/pxiii_bec/scripts/scanwrappers.py index 78a17df..0393d34 100644 --- a/pxiii_bec/scripts/scanwrappers.py +++ b/pxiii_bec/scripts/scanwrappers.py @@ -1,5 +1,4 @@ -# import bec -# import bec_lib.devicemanager.DeviceContainer as dev +# pylint: disable=undefined-variable def bl_check_beam(): -- 2.49.1 From e38269b96fe71f70d2fbbb7b6a7567adfd0b462d Mon Sep 17 00:00:00 2001 From: Unknown MX Person Date: Tue, 29 Apr 2025 16:04:29 +0200 Subject: [PATCH 09/11] ABR with latest BEC --- .../device_configs/x06da_device_config.yaml | 2 +- pxiii_bec/devices/A3200.py | 202 ++++++++++-------- 2 files changed, 112 insertions(+), 92 deletions(-) diff --git a/pxiii_bec/device_configs/x06da_device_config.yaml b/pxiii_bec/device_configs/x06da_device_config.yaml index 0f61b68..3221db5 100644 --- a/pxiii_bec/device_configs/x06da_device_config.yaml +++ b/pxiii_bec/device_configs/x06da_device_config.yaml @@ -57,7 +57,7 @@ sldi_sizex: softwareTrigger: false sldi_ceny: description: FE slit-diaphragm vertical center - deviceClass: ophyd_devices.EpicsMotorEC + deviceClass: ophyd.EpicsMotor deviceConfig: {prefix: 'X06DA-FE-SLDI:CENTERY'} onFailure: buffer enabled: true diff --git a/pxiii_bec/devices/A3200.py b/pxiii_bec/devices/A3200.py index 0303d8e..1241d1c 100644 --- a/pxiii_bec/devices/A3200.py +++ b/pxiii_bec/devices/A3200.py @@ -51,9 +51,9 @@ Examples """ import time -from ophyd import Component, EpicsSignal, EpicsSignalRO, Kind +from ophyd import Device, Component, EpicsSignal, EpicsSignalRO, Kind from ophyd.status import SubscriptionStatus -from ophyd_devices.interfaces.base_classes.bec_device_base import BECDeviceBase, CustomPrepare +from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase try: from .A3200enums import AbrCmd, AbrMode @@ -67,90 +67,7 @@ logger = bec_logger.logger # pylint: disable=logging-fstring-interpolation -class AerotechAbrMixin(CustomPrepare): - """Configuration class for the Aerotech A3200 controller for the ABR stage""" - - def on_stage(self): - """ - - NOTE: Zac's request is that stage is essentially ARM, i.e. get ready and don't do anything. - """ - - logger.warning(f"Configuring {self.parent.scaninfo.scan_msg.info['scan_name']} on ABR") - - d = {} - if self.parent.scaninfo.scan_type in ("measure", "measurement", "fly"): - scanargs = self.parent.scaninfo.scan_msg.info["kwargs"] - scanname = self.parent.scaninfo.scan_msg.info["scan_name"] - - if scanname in ( - "standardscan", - "helicalscan", - "helicalscan1", - "helicalscan2", - "helicalscan3", - ): - d["scan_command"] = AbrCmd.MEASURE_STANDARD - d["var_1"] = scanargs["start"] - d["var_2"] = scanargs["range"] - d["var_3"] = scanargs["move_time"] - d["var_4"] = scanargs.get("ready_rate", 500) - d["var_5"] = 0 - d["var_6"] = 0 - d["var_7"] = 0 - # d["var_8"] = 0 - # d["var_9"] = 0 - if scanname in ("verticallinescan", "vlinescan"): - d["scan_command"] = AbrCmd.VERTICAL_LINE_SCAN - d["var_1"] = scanargs["range"] / scanargs["steps"] - d["var_2"] = scanargs["steps"] - d["var_3"] = scanargs["exp_time"] - d["var_4"] = 0 - d["var_5"] = 0 - d["var_6"] = 0 - d["var_7"] = 0 - # d["var_8"] = 0 - # d["var_9"] = 0 - if scanname in ("screeningscan"): - d["scan_command"] = AbrCmd.SCREENING - d["var_1"] = scanargs["start"] - d["var_2"] = scanargs["oscrange"] - d["var_3"] = scanargs["exp_time"] - d["var_4"] = scanargs["range"] / scanargs["steps"] - d["var_5"] = scanargs["steps"] - d["var_6"] = scanargs.get("delta", 0.5) - d["var_7"] = 0 - # d["var_8"] = 0 - # d["var_9"] = 0 - if scanname in ("rasterscan", "rastersimplescan"): - d["scan_command"] = AbrCmd.RASTER_SCAN_SIMPLE - d["var_1"] = scanargs["exp_time"] - d["var_2"] = scanargs["range_x"] / scanargs["steps_x"] - d["var_3"] = scanargs["range_y"] / scanargs["steps_y"] - d["var_4"] = scanargs["steps_x"] - d["var_5"] = scanargs["steps_y"] - d["var_6"] = 0 - d["var_7"] = 0 - # d["var_8"] = 0 - # d["var_9"] = 0 - - # Reconfigure if got a valid scan config - if len(d) > 0: - self.parent.configure(d) - - # Stage the parent - self.parent.bluestage() - - def on_kickoff(self): - """Kick off parent""" - self.parent.bluekickoff() - - def on_unstage(self): - """Unstage the ABR controller""" - self.parent.blueunstage() - - -class AerotechAbrStage(BECDeviceBase): +class AerotechAbrStage(PSIDeviceBase, Device): """Standard PX stage on A3200 controller This is the wrapper class for the standard rotation stage layout for the PX @@ -161,8 +78,7 @@ class AerotechAbrStage(BECDeviceBase): it via 10+1 global variables. """ - custom_prepare_cls = AerotechAbrMixin - USER_ACCESS = ["reset", "kickoff", "bluekickoff", "complete", "set_axis_mode", "arm", "disarm"] + USER_ACCESS = ["reset", "kickoff", "complete", "set_axis_mode", "arm", "disarm"] taskStop = Component(EpicsSignal, "-AERO:TSK-STOP", put_complete=True, kind=Kind.omitted) status = Component(EpicsSignal, "-AERO:STAT", put_complete=True, kind=Kind.omitted) @@ -214,6 +130,30 @@ class AerotechAbrStage(BECDeviceBase): task4 = Component(EpicsSignalRO, "-AERO:TSK4-DONE", auto_monitor=True) scan_done = Component(EpicsSignal, "-GRD:SCAN-DONE", kind=Kind.config) + def __init__( + self, + prefix="", + *, + name, + kind=None, + read_attrs=None, + configuration_attrs=None, + parent=None, + scan_info=None, + **kwargs, + ): + # super() will call the mixin class + super().__init__( + prefix=prefix, + name=name, + kind=kind, + read_attrs=read_attrs, + configuration_attrs=configuration_attrs, + parent=parent, + scan_info=scan_info, + **kwargs, + ) + def set_axis_mode(self, mode: str, settle_time=0.1) -> None: """Set axis mode to direct/measurement mode. @@ -230,6 +170,86 @@ class AerotechAbrStage(BECDeviceBase): if mode == "measuring": self.axisAxesMode.set(AbrMode.MEASURING, settle_time=settle_time).wait() + def on_stage(self): + """ + + NOTE: Zac's request is that stage is essentially ARM, i.e. get ready and don't do anything. + """ + + logger.warning(f"Configuring {self.scaninfo.scan_msg.info['scan_name']} on ABR") + + d = {} + if self.scan_info.scan_type in ("measure", "measurement", "fly"): + # FIXME: I don't care about how we fish out config parameters from scan info + scan_args = { + **self.scan_info.msg.request_inputs["inputs"], + **self.scan_info.msg.request_inputs["kwargs"], + **self.scan_info.msg.scan_parameters, + } + scanname = self.scan_info.scan_msg.info["scan_name"] + + if scanname in ( + "standardscan", + "helicalscan", + "helicalscan1", + "helicalscan2", + "helicalscan3", + ): + d["scan_command"] = AbrCmd.MEASURE_STANDARD + d["var_1"] = scan_args["start"] + d["var_2"] = scan_args["range"] + d["var_3"] = scan_args["move_time"] + d["var_4"] = scan_args.get("ready_rate", 500) + d["var_5"] = 0 + d["var_6"] = 0 + d["var_7"] = 0 + # d["var_8"] = 0 + # d["var_9"] = 0 + if scanname in ("verticallinescan", "vlinescan"): + d["scan_command"] = AbrCmd.VERTICAL_LINE_SCAN + d["var_1"] = scan_args["range"] / scan_args["steps"] + d["var_2"] = scan_args["steps"] + d["var_3"] = scan_args["exp_time"] + d["var_4"] = 0 + d["var_5"] = 0 + d["var_6"] = 0 + d["var_7"] = 0 + # d["var_8"] = 0 + # d["var_9"] = 0 + if scanname in ("screeningscan"): + d["scan_command"] = AbrCmd.SCREENING + d["var_1"] = scan_args["start"] + d["var_2"] = scan_args["oscrange"] + d["var_3"] = scan_args["exp_time"] + d["var_4"] = scan_args["range"] / scan_args["steps"] + d["var_5"] = scan_args["steps"] + d["var_6"] = scan_args.get("delta", 0.5) + d["var_7"] = 0 + # d["var_8"] = 0 + # d["var_9"] = 0 + if scanname in ("rasterscan", "rastersimplescan"): + d["scan_command"] = AbrCmd.RASTER_SCAN_SIMPLE + d["var_1"] = scan_args["exp_time"] + d["var_2"] = scan_args["range_x"] / scan_args["steps_x"] + d["var_3"] = scan_args["range_y"] / scan_args["steps_y"] + d["var_4"] = scan_args["steps_x"] + d["var_5"] = scan_args["steps_y"] + d["var_6"] = 0 + d["var_7"] = 0 + # d["var_8"] = 0 + # d["var_9"] = 0 + + # Reconfigure if got a valid scan config + if len(d) > 0: + self.configure(d) + + # Stage the ABR stage + self.arm() + + def on_unstage(self): + """Unstage the ABR controller""" + self.disarm() + def configure(self, d: dict) -> tuple: """ " Configure the exposure scripts @@ -284,14 +304,14 @@ class AerotechAbrStage(BECDeviceBase): new = self.read_configuration() return old, new - def bluestage(self): + def arm(self): """Bluesky-style stage Since configuration synchronization is not guaranteed, this does nothing. The script launched by kickoff(). """ - def bluekickoff(self, timeout=1) -> SubscriptionStatus: + def on_kickoff(self, timeout=1) -> SubscriptionStatus: """Kick off the set program""" self.start_command.set(1).wait() @@ -304,7 +324,7 @@ class AerotechAbrStage(BECDeviceBase): status.wait() # return status - def blueunstage(self, settle_time=0.1): + def disarm(self, settle_time=0.1): """Stops current script and releases the axes""" # Disarm commands self.scan_command.set(AbrCmd.NONE, settle_time=settle_time).wait() -- 2.49.1 From e77f9af9ca5956d2873ec6238ac12c675fbe105d Mon Sep 17 00:00:00 2001 From: gac-x06da Date: Wed, 14 May 2025 13:01:41 +0200 Subject: [PATCH 10/11] At least no exceptions --- pxiii_bec/bec_widgets/auto_updates.py | 139 +++++++----------- .../device_configs/x06da_device_config.yaml | 4 +- pxiii_bec/devices/A3200.py | 128 ++++++++-------- 3 files changed, 119 insertions(+), 152 deletions(-) diff --git a/pxiii_bec/bec_widgets/auto_updates.py b/pxiii_bec/bec_widgets/auto_updates.py index ccecd00..003bd31 100644 --- a/pxiii_bec/bec_widgets/auto_updates.py +++ b/pxiii_bec/bec_widgets/auto_updates.py @@ -1,100 +1,71 @@ -from bec_widgets.cli.auto_updates import AutoUpdates, ScanInfo +from bec_widgets.widgets.containers.auto_update.auto_updates import AutoUpdates + +from bec_lib.messages import ScanStatusMessage from bec_widgets.cli.rpc.rpc_base import RPCResponseTimeoutError class PlotUpdate(AutoUpdates): - create_default_dock = True - enabled = True - _scan_msg = None - def do_update(self, msg): - """Save the original scan message for future use""" - self._scan_msg = msg - return super().do_update(msg) + ####################################################################### + ################# GUI Callbacks ####################################### + ####################################################################### - # def simple_line_scan(self, info: ScanInfo) -> None: - # """ - # Simple line scan. - # """ - # dev_x = info.scan_report_devices[0] - # dev_y = self.get_selected_device(info.monitored_devices, self.figure.selected_device) - # if not dev_y: - # return - # self.figure.clear_all() - # plt = self.figure.plot(dev_x, dev_y) - # plt.set(title=f"PXIII: Scan {info.scan_number}", x_label=dev_x, y_label=dev_y) - - def plot_handler(self, info: ScanInfo) -> None: - """Simple keyword handler for 'plot' - - This simple keyword handler looks for the keyword 'plot' in the scan arguments. - This allows the user to explictly specify the desired data source. Useful for alignment - scans. + def on_start(self) -> None: """ - print(info.scan_report_devices) + Procedure to run when the auto updates are enabled. + """ + self.start_default_dock() - dev_x = info.scan_report_devices[0] - if "kwargs" in self._scan_msg.info: - det = self._scan_msg.info["kwargs"].get("plot", None) - else: - det = None - if not det: - return + def on_stop(self) -> None: + """ + Procedure to run when the auto updates are disabled. + """ - plt1 = self.get_default_figure() - # clear_all() will throw RPCResponseTimeoutError - try: - plt1.clear_all() - except RPCResponseTimeoutError: - pass + def on_scan_open(self, msg: ScanStatusMessage) -> None: + """ + Procedure to run when a scan starts. - # plot() will throw RPCResponseTimeoutError - try: - if len(info.scan_report_devices) == 2: - # 2D plot - dev_x = info.scan_report_devices[0] - dev_y = info.scan_report_devices[1] - plt1.plot( + Args: + msg (ScanStatusMessage): The scan status message. + """ + if msg.scan_name == "line_scan" and msg.scan_report_devices: + return self.simple_line_scan(msg) + if msg.scan_name == "grid_scan" and msg.scan_report_devices: + return self.simple_grid_scan(msg) + + dev_x = msg.scan_report_devices[0] + if "kwargs" in msg.request_inputs: + dev_y = msg.request_inputs["kwargs"].get("plot", None) + if dev_y is not None: + # Set the dock to the waveform widget + wf = self.set_dock_to_widget("Waveform") + + # Clear the waveform widget and plot the data + wf.clear_all() + wf.plot( x_name=dev_x, y_name=dev_y, - z_name=det, - title=f"Scan {info.scan_number}", + label=f"Scan {msg.info.scan_number} - {dev_y}", + title=f"Scan {msg.info.scan_number}", x_label=dev_x, y_label=dev_y, - z_label=det, ) - elif len(info.scan_report_devices) == 1: - # 1D plot - dev_x = info.scan_report_devices[0] - plt1.plot( - x_name=dev_x, - y_name=det, - title=f"Scan {info.scan_number}", - x_label=dev_x, - y_label=det, - ) - else: - # Default is 1D - dev_x = info.scan_report_devices[0] - plt1.plot( - x_name=dev_x, - y_name=det, - title=f"Scan {info.scan_number}", - x_label=dev_x, - y_label=det, - ) - except RPCResponseTimeoutError: - pass - # plt1.add_dap(dev_x, det, dap="LinearModel") + elif msg.scan_report_devices: + return self.best_effort(msg) + return None - def handler(self, info: ScanInfo) -> None: - """Dock configuration handler""" - # EXAMPLES: - # if info.scan_name == "line_scan" and info.scan_report_devices: - # self.simple_line_scan(info) - # return - # if info.scan_name == "grid_scan" and info.scan_report_devices: - # self.run_grid_scan_update(info) - # return - super().handler(info) - self.plot_handler(info) + def on_scan_closed(self, msg: ScanStatusMessage) -> None: + """ + Procedure to run when a scan ends. + + Args: + msg (ScanStatusMessage): The scan status message. + """ + + def on_scan_abort(self, msg: ScanStatusMessage) -> None: + """ + Procedure to run when a scan is aborted. + + Args: + msg (ScanStatusMessage): The scan status message. + """ \ No newline at end of file diff --git a/pxiii_bec/device_configs/x06da_device_config.yaml b/pxiii_bec/device_configs/x06da_device_config.yaml index 3221db5..5f2ecb4 100644 --- a/pxiii_bec/device_configs/x06da_device_config.yaml +++ b/pxiii_bec/device_configs/x06da_device_config.yaml @@ -152,7 +152,7 @@ fi1_try: dccm_theta1: description: Monochromator pitch 1 deviceClass: ophyd.EpicsMotor - deviceConfig: {prefix: 'X06DA-OP-DCCM:PITCH1'} + deviceConfig: {prefix: 'X06DA-OP-DCCM:THETA1'} onFailure: buffer enabled: true readoutPriority: monitored @@ -179,7 +179,7 @@ dccm_diode_bottom: dccm_theta2: description: Monochromator pitch 2 deviceClass: ophyd.EpicsMotor - deviceConfig: {prefix: 'X06DA-OP-DCCM:PITCH2'} + deviceConfig: {prefix: 'X06DA-OP-DCCM:THETA2'} onFailure: buffer enabled: true readoutPriority: monitored diff --git a/pxiii_bec/devices/A3200.py b/pxiii_bec/devices/A3200.py index 1241d1c..4e9941e 100644 --- a/pxiii_bec/devices/A3200.py +++ b/pxiii_bec/devices/A3200.py @@ -175,76 +175,72 @@ class AerotechAbrStage(PSIDeviceBase, Device): NOTE: Zac's request is that stage is essentially ARM, i.e. get ready and don't do anything. """ - - logger.warning(f"Configuring {self.scaninfo.scan_msg.info['scan_name']} on ABR") - d = {} - if self.scan_info.scan_type in ("measure", "measurement", "fly"): - # FIXME: I don't care about how we fish out config parameters from scan info - scan_args = { - **self.scan_info.msg.request_inputs["inputs"], - **self.scan_info.msg.request_inputs["kwargs"], - **self.scan_info.msg.scan_parameters, - } - scanname = self.scan_info.scan_msg.info["scan_name"] + # FIXME: I don't care about how we fish out config parameters from scan info + scan_args = { + **self.scan_info.msg.request_inputs["inputs"], + **self.scan_info.msg.request_inputs["kwargs"], + **self.scan_info.msg.scan_parameters, + } + scanname = self.scan_info.msg.scan_name - if scanname in ( - "standardscan", - "helicalscan", - "helicalscan1", - "helicalscan2", - "helicalscan3", - ): - d["scan_command"] = AbrCmd.MEASURE_STANDARD - d["var_1"] = scan_args["start"] - d["var_2"] = scan_args["range"] - d["var_3"] = scan_args["move_time"] - d["var_4"] = scan_args.get("ready_rate", 500) - d["var_5"] = 0 - d["var_6"] = 0 - d["var_7"] = 0 - # d["var_8"] = 0 - # d["var_9"] = 0 - if scanname in ("verticallinescan", "vlinescan"): - d["scan_command"] = AbrCmd.VERTICAL_LINE_SCAN - d["var_1"] = scan_args["range"] / scan_args["steps"] - d["var_2"] = scan_args["steps"] - d["var_3"] = scan_args["exp_time"] - d["var_4"] = 0 - d["var_5"] = 0 - d["var_6"] = 0 - d["var_7"] = 0 - # d["var_8"] = 0 - # d["var_9"] = 0 - if scanname in ("screeningscan"): - d["scan_command"] = AbrCmd.SCREENING - d["var_1"] = scan_args["start"] - d["var_2"] = scan_args["oscrange"] - d["var_3"] = scan_args["exp_time"] - d["var_4"] = scan_args["range"] / scan_args["steps"] - d["var_5"] = scan_args["steps"] - d["var_6"] = scan_args.get("delta", 0.5) - d["var_7"] = 0 - # d["var_8"] = 0 - # d["var_9"] = 0 - if scanname in ("rasterscan", "rastersimplescan"): - d["scan_command"] = AbrCmd.RASTER_SCAN_SIMPLE - d["var_1"] = scan_args["exp_time"] - d["var_2"] = scan_args["range_x"] / scan_args["steps_x"] - d["var_3"] = scan_args["range_y"] / scan_args["steps_y"] - d["var_4"] = scan_args["steps_x"] - d["var_5"] = scan_args["steps_y"] - d["var_6"] = 0 - d["var_7"] = 0 - # d["var_8"] = 0 - # d["var_9"] = 0 + if scanname in ( + "standardscan", + "helicalscan", + "helicalscan1", + "helicalscan2", + "helicalscan3", + ): + d["scan_command"] = AbrCmd.MEASURE_STANDARD + d["var_1"] = scan_args["start"] + d["var_2"] = scan_args["range"] + d["var_3"] = scan_args["move_time"] + d["var_4"] = scan_args.get("ready_rate", 500) + d["var_5"] = 0 + d["var_6"] = 0 + d["var_7"] = 0 + # d["var_8"] = 0 + # d["var_9"] = 0 + if scanname in ("verticallinescan", "vlinescan"): + d["scan_command"] = AbrCmd.VERTICAL_LINE_SCAN + d["var_1"] = scan_args["range"] / scan_args["steps"] + d["var_2"] = scan_args["steps"] + d["var_3"] = scan_args["exp_time"] + d["var_4"] = 0 + d["var_5"] = 0 + d["var_6"] = 0 + d["var_7"] = 0 + # d["var_8"] = 0 + # d["var_9"] = 0 + if scanname in ("screeningscan"): + d["scan_command"] = AbrCmd.SCREENING + d["var_1"] = scan_args["start"] + d["var_2"] = scan_args["oscrange"] + d["var_3"] = scan_args["exp_time"] + d["var_4"] = scan_args["range"] / scan_args["steps"] + d["var_5"] = scan_args["steps"] + d["var_6"] = scan_args.get("delta", 0.5) + d["var_7"] = 0 + # d["var_8"] = 0 + # d["var_9"] = 0 + if scanname in ("rasterscan", "rastersimplescan"): + d["scan_command"] = AbrCmd.RASTER_SCAN_SIMPLE + d["var_1"] = scan_args["exp_time"] + d["var_2"] = scan_args["range_x"] / scan_args["steps_x"] + d["var_3"] = scan_args["range_y"] / scan_args["steps_y"] + d["var_4"] = scan_args["steps_x"] + d["var_5"] = scan_args["steps_y"] + d["var_6"] = 0 + d["var_7"] = 0 + # d["var_8"] = 0 + # d["var_9"] = 0 - # Reconfigure if got a valid scan config - if len(d) > 0: - self.configure(d) + # Reconfigure if got a valid scan config + if len(d) > 0: + self.configure(d) - # Stage the ABR stage - self.arm() + # Stage the ABR stage + self.arm() def on_unstage(self): """Unstage the ABR controller""" -- 2.49.1 From fc4e33ac93d22d9e9a513c99cb4698b11e4a1465 Mon Sep 17 00:00:00 2001 From: gac-x06da Date: Wed, 21 May 2025 11:00:08 +0200 Subject: [PATCH 11/11] WIP --- pxiii_bec/bec_widgets/ComissioningGUIDraft.ui | 135 +++++++++++++ pxiii_bec/bec_widgets/widgets/client.py | 40 ++++ .../widgets/scan_history/__init__.py | 0 .../scan_history/register_scan_history.py | 15 ++ .../widgets/scan_history/scan_history.py | 191 ++++++++++++++++++ .../scan_history/scan_history.pyproject | 1 + .../widgets/scan_history/scan_history.ui | 115 +++++++++++ .../scan_history/scan_history_plugin.py | 54 +++++ pxiii_bec/scripts/alignment_fit.py | 49 +++++ pxiii_bec/scripts/kat.py | 30 +++ 10 files changed, 630 insertions(+) create mode 100644 pxiii_bec/bec_widgets/ComissioningGUIDraft.ui create mode 100644 pxiii_bec/bec_widgets/widgets/client.py create mode 100644 pxiii_bec/bec_widgets/widgets/scan_history/__init__.py create mode 100644 pxiii_bec/bec_widgets/widgets/scan_history/register_scan_history.py create mode 100644 pxiii_bec/bec_widgets/widgets/scan_history/scan_history.py create mode 100644 pxiii_bec/bec_widgets/widgets/scan_history/scan_history.pyproject create mode 100644 pxiii_bec/bec_widgets/widgets/scan_history/scan_history.ui create mode 100644 pxiii_bec/bec_widgets/widgets/scan_history/scan_history_plugin.py create mode 100644 pxiii_bec/scripts/alignment_fit.py create mode 100644 pxiii_bec/scripts/kat.py diff --git a/pxiii_bec/bec_widgets/ComissioningGUIDraft.ui b/pxiii_bec/bec_widgets/ComissioningGUIDraft.ui new file mode 100644 index 0000000..be17da6 --- /dev/null +++ b/pxiii_bec/bec_widgets/ComissioningGUIDraft.ui @@ -0,0 +1,135 @@ + + + Form + + + + 0 + 0 + 1801 + 1459 + + + + + 1801 + 1459 + + + + Form + + + + + + 0 + + + + Control Panel + + + + + + + + + + + + + + + + + + + Logbook + + + + + + + 24 + + + + Coming soon... + + + + + + + + Take a break + + + + + + + + + Qt::Orientation::Horizontal + + + + 1073 + 20 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + ScanControl + QWidget +
scan_control
+
+ + Waveform + QWidget +
waveform
+
+ + BECQueue + QWidget +
bec_queue
+
+ + Minesweeper + QWidget +
minesweeper
+
+ + ScanHistory + QWidget +
scan_history
+
+
+ + +
diff --git a/pxiii_bec/bec_widgets/widgets/client.py b/pxiii_bec/bec_widgets/widgets/client.py new file mode 100644 index 0000000..abc25f8 --- /dev/null +++ b/pxiii_bec/bec_widgets/widgets/client.py @@ -0,0 +1,40 @@ +# This file was automatically generated by generate_cli.py +# type: ignore + +from __future__ import annotations + +from bec_lib.logger import bec_logger + +from bec_widgets.cli.rpc.rpc_base import RPCBase, rpc_call + +logger = bec_logger.logger + +# pylint: skip-file + + +_Widgets = { + "ScanHistory": "ScanHistory", +} + + +class ScanHistory(RPCBase): + @rpc_call + def select_scan_from_history(self, value: "int") -> "None": + """ + Set scan from CLI. + + Args: + value (int) : value from history -1 ...-10000 + """ + + @rpc_call + def add_scan_from_history(self) -> "None": + """ + Load selected scan from history. + """ + + @rpc_call + def clear_plot(self) -> "None": + """ + Delete all curves on the plot. + """ diff --git a/pxiii_bec/bec_widgets/widgets/scan_history/__init__.py b/pxiii_bec/bec_widgets/widgets/scan_history/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pxiii_bec/bec_widgets/widgets/scan_history/register_scan_history.py b/pxiii_bec/bec_widgets/widgets/scan_history/register_scan_history.py new file mode 100644 index 0000000..8de6b85 --- /dev/null +++ b/pxiii_bec/bec_widgets/widgets/scan_history/register_scan_history.py @@ -0,0 +1,15 @@ +def main(): # pragma: no cover + from qtpy import PYSIDE6 + + if not PYSIDE6: + print("PYSIDE6 is not available in the environment. Cannot patch designer.") + return + from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection + + from pxiii_bec.bec_widgets.widgets.scan_history.scan_history_plugin import ScanHistoryPlugin + + QPyDesignerCustomWidgetCollection.addCustomWidget(ScanHistoryPlugin()) + + +if __name__ == "__main__": # pragma: no cover + main() diff --git a/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.py b/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.py new file mode 100644 index 0000000..33072f8 --- /dev/null +++ b/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +import os +from typing import TYPE_CHECKING, TypedDict + +from bec_lib.logger import bec_logger +from bec_qthemes import material_icon +from bec_widgets.utils.bec_widget import BECWidget + + +from bec_widgets.utils.error_popups import SafeSlot +from bec_widgets.utils.ui_loader import UILoader +from qtpy.QtWidgets import QVBoxLayout, QWidget + +logger = bec_logger.logger + +if TYPE_CHECKING: + from qtpy.QtWidgets import QPushButton, QLabel, QSpinBox + from bec_widgets.widgets.plots.waveform.waveform import Waveform + from bec_widgets.widgets.control.device_input.device_combobox.device_combobox import DeviceComboBox + from bec_widgets.widgets.editors.text_box.text_box import TextBox + + +class ScanHistoryUIComponents(TypedDict): + waveform: Waveform + metadata_text_box: TextBox + monitor_label: QLabel + monitor_combobox: DeviceComboBox + history_label: QLabel + history_spin_box: QSpinBox + history_add: QPushButton + history_clear: QPushButton + + +class ScanHistory(BECWidget, QWidget): + USER_ACCESS = ["select_scan_from_history", "add_scan_from_history", "clear_plot"] + PLUGIN = True + ui_file = "./scan_history.ui" + components: ScanHistoryUIComponents + + def __init__(self, parent=None, **kwargs): + super().__init__(parent=parent, **kwargs) + self._load_ui() + + def _load_ui(self): + current_path = os.path.dirname(__file__) + self.ui = UILoader(self).loader(os.path.join(current_path, self.ui_file)) + layout = QVBoxLayout() + layout.addWidget(self.ui) + self.setLayout(layout) + + self.components: ScanHistoryUIComponents = { + "waveform" : self.ui.waveform, + "metadata_text_box" : self.ui.metadata_text_box, + "monitor_label" : self.ui.monitor_label, + "monitor_combobox" : self.ui.monitor_combobox, + "history_label" : self.ui.history_label, + "history_spin_box" : self.ui.history_spin_box, + "history_add" : self.ui.history_add, + "history_clear" : self.ui.history_clear, + } + + icon_options = {"size": (16, 16), "convert_to_pixmap": False} + + self.components['monitor_combobox'].apply_filter = False + self.components['monitor_combobox'].devices = ['dccm_diode_bottom', 'dccm_diode_top'] + + self.components['history_spin_box'].setMinimum(-10000) + self.components['history_spin_box'].setMaximum(-1) + self.components['history_spin_box'].valueChanged.connect(self._scan_history_selected) + self._scan_history_selected(-1) + self.components['history_spin_box'].setValue(-1) + self.components['history_add'].setText("Load") + self.components['history_add'].setStyleSheet( + "background-color: #129490; color: white; font-weight: bold; font-size: 12px;" + ) + + self.components['history_clear'].setText("Clear") + self.components['history_clear'].setStyleSheet( + "background-color: #065143; color: white; font-weight: bold; font-size: 12px;" + ) + + self.components['history_add'].clicked.connect(self._refresh_plot) + self.components['history_clear'].clicked.connect(self.clear_plot) + self.setWindowTitle("Scan History") + self._scan_history_selected(-1) + + @SafeSlot() + def add_scan_from_history(self) -> None: + """Load selected scan from history.""" + self.components['history_add'].click() + + @SafeSlot() + def clear_plot(self) -> None: + """Delete all curves on the plot.""" + self.components['waveform'].clear_all() + + @SafeSlot() + def _refresh_plot(self) -> None: + """Refresh plot.""" + spin_box_value = self.components['history_spin_box'].value() + self._check_scan_in_history(spin_box_value) + + # Get the data from the client + data = self.client.history[spin_box_value] + + # Check that the plot does not already have a curve with the same data + scan_number = int(data.metadata.bec['scan_number']) + monitor_name = self.components['monitor_combobox'].currentText() + # Get signal hints + signal_name = getattr(self.client.device_manager.devices, monitor_name)._hints + signal_name = signal_name[0] if len(signal_name)>0 else signal_name + + curve_label = f"Scan-{scan_number}-{monitor_name}-{signal_name}" + if len([curve for curve in self.components['waveform'].curves if curve.config.label == curve_label]): + return + if not hasattr(data.devices, monitor_name): + raise ValueError(f"Device {monitor_name} not found in data.") + + # Get scan motors and check that the plot x_axis motor is the same as the scan motor, if not, clear the plot + scan_motors = [motor.decode() for motor in data.metadata.bec['scan_motors']] + x_motor_name = self.components['waveform'].x_mode + if x_motor_name not in scan_motors: + self.clear_plot() + self.components['waveform'].x_mode = x_motor_name = scan_motors[0] + + # fetching the data + monitor_data = getattr(data.devices, monitor_name).read()[signal_name]['value'] + motor_data = getattr(data.devices, x_motor_name).read()[x_motor_name]['value'] + + # Plot custom curve, with custom label + self.components['waveform'].plot(x=motor_data, y=monitor_data, label=curve_label) + x_label = f"{x_motor_name} / [{getattr(self.client.device_manager.devices, x_motor_name).egu()}]" + self.components['waveform'].x_label = x_label + + def _check_scan_in_history(self, history_value:int) -> None: + """ + Check if scan is in history. + + Args: + history_value (int): Value from history -1...-10000 + """ + if len(self.client.history) < abs(history_value): + self.components['metadata_text_box'].set_plain_text(f"Scan history does not have the request scan {history_value} of history with length: {len(self.client.history)}") + return + + + def select_scan_from_history(self, value:int) -> None: + """ + Set scan from CLI. + + Args: + value (int) : value from history -1 ...-10000 + """ + if value >=0: + raise ValueError(f"Value must be smaller or equal -1, provided {value}") + self.components['history_spin_box'].setValue(value) + + @SafeSlot(int) + def _scan_history_selected(self, spin_box_value:int) -> None: + self._check_scan_in_history(spin_box_value) + data = self.client.history[spin_box_value] + data.metadata.bec['scan_motors'][0].decode() + + text = str(data) + scan_motor_text = "\n" + "Scan Motors: " + for motor in data.metadata.bec['scan_motors']: + scan_motor_text += f" {motor.decode()}" + + self.components['metadata_text_box'].set_plain_text(text + scan_motor_text) + + @SafeSlot(str) + def _set_x_axis(self, device_x:str) -> None: + self.components['waveform'].x_mode = device_x + + @SafeSlot(str) + def _plot_new_device(self, device:str) -> None: + # if len(curve for curve in self.components["waveform"].curves if curve.config.label == f"{device}-{device}": + self.components["waveform"].plot(device) + + +if __name__ == "__main__": # pragma: no cover + import sys + + from qtpy.QtWidgets import QApplication + + app = QApplication(sys.argv) + widget = ScanHistory() + + widget.show() + sys.exit(app.exec_()) diff --git a/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.pyproject b/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.pyproject new file mode 100644 index 0000000..591325d --- /dev/null +++ b/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.pyproject @@ -0,0 +1 @@ +{'files': ['scan_history.py']} \ No newline at end of file diff --git a/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.ui b/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.ui new file mode 100644 index 0000000..a1ba889 --- /dev/null +++ b/pxiii_bec/bec_widgets/widgets/scan_history/scan_history.ui @@ -0,0 +1,115 @@ + + + Form + + + + 0 + 0 + 955 + 796 + + + + Form + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + + + + + BPM Monitor + + + + + + + + + + Scan History + + + + + + + + + + Add scan + + + + + + + clear all + + + + + + + + + + 0 + 0 + + + + + 795 + 191 + + + + + + + + + + + TextBox + QWidget +
text_box
+
+ + DeviceComboBox + QComboBox +
device_combobox
+
+ + Waveform + QWidget +
waveform
+
+
+ + +
diff --git a/pxiii_bec/bec_widgets/widgets/scan_history/scan_history_plugin.py b/pxiii_bec/bec_widgets/widgets/scan_history/scan_history_plugin.py new file mode 100644 index 0000000..51a986f --- /dev/null +++ b/pxiii_bec/bec_widgets/widgets/scan_history/scan_history_plugin.py @@ -0,0 +1,54 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from qtpy.QtDesigner import QDesignerCustomWidgetInterface + +from bec_widgets.utils.bec_designer import designer_material_icon +from pxiii_bec.bec_widgets.widgets.scan_history.scan_history import ScanHistory + +DOM_XML = """ + + + + +""" + + +class ScanHistoryPlugin(QDesignerCustomWidgetInterface): # pragma: no cover + def __init__(self): + super().__init__() + self._form_editor = None + + def createWidget(self, parent): + t = ScanHistory(parent) + return t + + def domXml(self): + return DOM_XML + + def group(self): + return "" + + def icon(self): + return designer_material_icon(ScanHistory.ICON_NAME) + + def includeFile(self): + return "scan_history" + + def initialize(self, form_editor): + self._form_editor = form_editor + + def isContainer(self): + return False + + def isInitialized(self): + return self._form_editor is not None + + def name(self): + return "ScanHistory" + + def toolTip(self): + return "ScanHistory" + + def whatsThis(self): + return self.toolTip() diff --git a/pxiii_bec/scripts/alignment_fit.py b/pxiii_bec/scripts/alignment_fit.py new file mode 100644 index 0000000..5cd0418 --- /dev/null +++ b/pxiii_bec/scripts/alignment_fit.py @@ -0,0 +1,49 @@ +import numpy as np +from scipy.ndimage import gaussian_filter1d +from lmfit.models import GaussianModel + + +def alignment_fit_and_plot( + history_index: int, + device_name: str, + signal_name: str | None = None, + smoothing_sigma: float = 2.0, +): + """ + Get data for a completed scan from the BEC history, apply smoothing, gaussian fit, + gradient, and plot all the results. + + Args: + history_index (int): scan to fetch, e.g. -1 for the most recent scan + device_name (str): the device for which to get the monitoring data + + """ + # Fetch scan data from the history + # by default, signal = device name, unless otherwise specified + signal = signal_name or device_name + scan = bec.history[history_index] + md = scan.metadata["bec"] + data = scan.devices[device_name][signal].read()["value"] + # motor name is a bytes object in the metadata, so make a string + motor_name = md["scan_motors"][0].decode() + + # Create a plot and a text box to display results + dock_area = bec.gui.new() + wf = dock_area.new().new(bec.gui.available_widgets.Waveform) + wf.title = f"Scan {md['scan_number']}: {md['scan_name']} of {motor_name}" + text = dock_area.new(position="right").new(widget=bec.gui.available_widgets.TextBox) + + # Calculate some processed data and add everything to the plot + wf.plot(data, label="Raw data") + smoothed_data = gaussian_filter1d(data, smoothing_sigma) + wf.plot(smoothed_data, label="Smoothed") + gradient = np.gradient(smoothed_data) + wf.plot(gradient, label="gradient") + + # Fit a Gaussian model to the smoothed data and show the fitting parameters in the textbox + x_data = scan.devices[motor_name][motor_name].read()["value"] + model = GaussianModel() + result = model.fit(smoothed_data, x=x_data) + text.set_plain_text(f"Fit parameters: \n{result.params.pretty_repr()}") + + return result diff --git a/pxiii_bec/scripts/kat.py b/pxiii_bec/scripts/kat.py new file mode 100644 index 0000000..423a483 --- /dev/null +++ b/pxiii_bec/scripts/kat.py @@ -0,0 +1,30 @@ +def scan_theta2(scan_start, scan_end, stepno, exp): + # Save the motor starting position + start_value = dev.dccm_theta2.read()['dccm_theta2']['value'] + print(f"Motor position is {start_value}") + + # Run the scan + s = scans.line_scan(dev.dccm_theta2, scan_start, scan_end, steps=stepno, exp_time=exp, relative=True) + + # data = s.devices[dccm_xbpm][dccm_xbpm].read()["value"] + + # Move motor back to starting position and print XBPM reading + umv(dev.dccm_theta2, start_value) + xbpm_reading = dev.dccm_xbpm.read()['dccm_xbpm']['value'] + print(f"Moving dccm_theta2 back to start position of where XBPM Reading is {xbpm_reading}") + end_value = dev.dccm_theta2.read()['dccm_theta2']['value'] + print(f"Motor was at {start_value} before the scan, now at {end_value}") + + # # Create a plot to display the results + dock_area = bec.gui.new() + wf = dock_area.new().new(bec.gui.available_widgets.Waveform) + wf.title = f"Scan of DCCM_theta2" + wf.plot(x_name='dccm_theta2', y_name='dccm_xbpm') + wf.add_dap_curve(device_label='dccm_xbpm-dccm_xbpm', dap_name='GaussianModel') + print(dap_xbpm.dap_params) + + + + + + -- 2.49.1