From d681a828788c719f06fa258cfedc1a6fd26e8599 Mon Sep 17 00:00:00 2001 From: gac-x05la Date: Mon, 21 Oct 2024 12:16:41 +0200 Subject: [PATCH] Minor fixes --- .../devices/aerotech/AerotechAutomation1.py | 5 ++ .../AerotechSnapAndStepTemplate.ascript | 36 ++++++++----- tomcat_bec/devices/gigafrost/stddaq_client.py | 3 ++ tomcat_bec/scans/tomcat_scanbase.py | 54 +++++++++++-------- tomcat_bec/scripts/demoscans.py | 44 ++++++--------- 5 files changed, 78 insertions(+), 64 deletions(-) diff --git a/tomcat_bec/devices/aerotech/AerotechAutomation1.py b/tomcat_bec/devices/aerotech/AerotechAutomation1.py index 3678458..b77bc85 100644 --- a/tomcat_bec/devices/aerotech/AerotechAutomation1.py +++ b/tomcat_bec/devices/aerotech/AerotechAutomation1.py @@ -970,6 +970,8 @@ class aa1AxisDriveDataCollection(Device): # This allocates the memory... self.npoints.set(npoints).wait() + # Reset incremental readback + self._switch.set(2).wait() new = self.read_configuration() return (old, new) @@ -993,6 +995,9 @@ class aa1AxisDriveDataCollection(Device): status.set_finished() return status + def reset(self): + """Reset incremental readback""" + self._switch.set("ResetRB", settle_time=0.1).wait() def complete(self, settle_time=0.1) -> DeviceStatus: """DDC just reads back whatever is available in the buffers""" diff --git a/tomcat_bec/devices/aerotech/AerotechSnapAndStepTemplate.ascript b/tomcat_bec/devices/aerotech/AerotechSnapAndStepTemplate.ascript index 320975f..5ebfc2c 100644 --- a/tomcat_bec/devices/aerotech/AerotechSnapAndStepTemplate.ascript +++ b/tomcat_bec/devices/aerotech/AerotechSnapAndStepTemplate.ascript @@ -8,12 +8,16 @@ program // External parameters var $fStartPosition as real = {{ scan.startpos }} var $iNumSteps as integer = {{ scan.numsteps }} + var $iNumBurst as integer = {{ scan.numburst or 1 }} var $fStepSize as real = {{ scan.stepsize }} var $fExposureTimeSec as real = {{ scan.exptime }} - var $fSettlingTimeSec as real = {{ scan.settle }} + var $fSettlingTimeSec as real = {{ scan.settling }} var $fVelJog as real = {{ scan.travel or 200 }} var $fVelScan as real = {{ scan.velocity or 50 }} var $fAcceleration = {{ scan.acceleration or 500 }} + var $eDdcTrigger as DriveDataCaptureTrigger = DriveDataCaptureTrigger.{{ scan.psotrigger }} + var $iNumDdcRead as integer = {{ scan.npoints }} + // Internal var $axis as axis = ROTY @@ -25,8 +29,7 @@ program SetupAxisRampValue($axis,0,$fAcceleration) // Move to start position before the scan (use soft-wait to catch errors) - var $fPosNext as real = $fStartPosition - MoveAbsolute($axis, $fPosNext, $fVelJog) + MoveAbsolute($axis, $fStartPosition, $fVelJog) WaitForMotionDone($axis) // Mandatory error checking after each movement @@ -35,27 +38,33 @@ program TaskSetError(TaskGetIndex(), "AxisFault on axis ROTY") end - // Configure PSO (to manual event generation) + // Turn off PSO + PsoDistanceConfigureInputs($axis, [PsoDistanceInput.XC4PrimaryFeedback]) + PsoDistanceCounterOff($axis) PsoDistanceEventsOff($axis) PsoWindowConfigureEvents($axis, PsoWindowEventMode.None) - PsoWaveformConfigurePulseFixedTotalTime($axis, 50) - PsoWaveformConfigurePulseFixedOnTime($axis, 20) - PsoWaveformConfigurePulseFixedCount($axis, 1) - PsoWaveformApplyPulseConfiguration($axis) + + // Configure PSO (to manual event generation in pulsed mode) PsoWaveformConfigureMode($axis, PsoWaveformMode.Pulse) + PsoWaveformConfigurePulseFixedTotalTime($axis, 100) + PsoWaveformConfigurePulseFixedOnTime($axis, 50) + PsoWaveformConfigurePulseFixedCount($axis, $iNumBurst) + PsoWaveformApplyPulseConfiguration($axis) + PsoWaveformOn($axis) + + // Activete PSO PsoOutputConfigureSource($axis, PsoOutputSource.Waveform) - PsoWaveformOn($axis) // Configure Drive Data Collection var $iDdcArrayAddr as integer = 8388608 - var $iDdcArraySize as integer = iMaximum(5000, $iNumSteps) + var $iDdcArraySize as integer = $iNumDdcRead var $iDdcSafeSpace as integer = 4096 DriveDataCaptureConfigureInput($axis, 0, DriveDataCaptureInput.PrimaryFeedback); DriveDataCaptureConfigureInput($axis, 1, DriveDataCaptureInput.AnalogInput0 ); - DriveDataCaptureConfigureTrigger($axis, 0, DriveDataCaptureTrigger.PsoOutput ); - DriveDataCaptureConfigureTrigger($axis, 1, DriveDataCaptureTrigger.PsoOutput ); + DriveDataCaptureConfigureTrigger($axis, 0, $eDdcTrigger ); + DriveDataCaptureConfigureTrigger($axis, 1, $eDdcTrigger ); DriveDataCaptureConfigureArray($axis, 0, $iDdcArrayAddr, $iDdcArraySize); DriveDataCaptureConfigureArray($axis, 1, $iDdcArrayAddr + $iDdcSafeSpace + 8 * $iDdcArraySize, $iDdcArraySize); @@ -71,7 +80,7 @@ program /////////////////////////////////////////////////////////// $iglobal[2] = $iNumSteps for $ii = 0 to ($iNumSteps-1) - MoveAbsolute($axis, $fPosNext, $fVelScan) + MoveAbsolute($axis, $fStartPosition + $ii * $fStepSize, $fVelScan) WaitForMotionDone($axis) $axisFaults = StatusGetAxisItem($axis, AxisDataSignal.AxisFault) @@ -82,7 +91,6 @@ program Dwell($fSettlingTimeSec) PsoEventGenerateSingle($axis) Dwell($fExposureTimeSec) - $fPosNext = $fPosNext + $fStepSize $iglobal[1] = $ii end diff --git a/tomcat_bec/devices/gigafrost/stddaq_client.py b/tomcat_bec/devices/gigafrost/stddaq_client.py index 3209d6f..ffe35ad 100644 --- a/tomcat_bec/devices/gigafrost/stddaq_client.py +++ b/tomcat_bec/devices/gigafrost/stddaq_client.py @@ -248,6 +248,9 @@ class StdDaqClient(Device): return except (ConnectionClosedError, ConnectionClosedOK): return + except AssertionError: + # Libraty throws after connection is closed + return finally: self._mon = None diff --git a/tomcat_bec/scans/tomcat_scanbase.py b/tomcat_bec/scans/tomcat_scanbase.py index 5920766..6b917d8 100644 --- a/tomcat_bec/scans/tomcat_scanbase.py +++ b/tomcat_bec/scans/tomcat_scanbase.py @@ -119,13 +119,14 @@ class TemplatedScanBase(AsyncFlyScanBase): yield from self.stubs.send_rpc_and_wait("es1_tasks", "stage") if self.camera is not None: yield from self.stubs.send_rpc_and_wait(self.camera, "stage") - if self.daqname is not None: - yield from self.stubs.send_rpc_and_wait(self.daqname, "stage") + # DAQ should be staged from script + # if self.daqname is not None: + # yield from self.stubs.send_rpc_and_wait(self.daqname, "stage") if self.preview is not None: yield from self.stubs.send_rpc_and_wait(self.preview, "stage") # TODO : This should stage the entire beamline - yield from super().stage() + # yield from super().stage() def scan_core(self): @@ -156,10 +157,10 @@ class TemplatedScanBase(AsyncFlyScanBase): time.sleep(0.5) # 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}") + # 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""" @@ -236,9 +237,12 @@ class GigaStepScanBase(ScanBase): required_kwargs = ["scan_start", "scan_end","steps"] gui_config = { "Movement parameters": ["steps"], - "Acquisition parameters" : ["exp_time", "burst_at_each_point"] + "Acquisition parameters" : ["exp_time", "burst_at_each_point", "roix", "roiy"] } + def _get_scan_motors(self): + self.scan_motors = ["es1_roty"] + def __init__( self, scan_start: float, @@ -247,6 +251,8 @@ class GigaStepScanBase(ScanBase): exp_time = 0.005, settling_time = 0.2, burst_at_each_point = 1, + roix = 2016, + roiy = 2016, sync='event', **kwargs): super().__init__( @@ -265,6 +271,8 @@ class GigaStepScanBase(ScanBase): self.scan_stepsize = (scan_end - scan_start) / steps self.scan_ntotal = burst_at_each_point * (steps + 1) self.scan_nburst = burst_at_each_point + self.scan_roix = roix + self.scan_roiy = roiy # Override wait time with actual latency # NOTE : The BEC does not wait for trigger() status done @@ -282,14 +290,13 @@ class GigaStepScanBase(ScanBase): "nimages": burst_at_each_point, "exposure": 1000*exp_time, "period": 2000*exp_time, - "pixel_width": 2016, - "pixel_height": 2016 + "pixel_width": self.scan_roix, + "pixel_height": self.scan_roiy } def _calculate_positions(self) -> None: """ Pre-calculate scan positions """ - self.positions = [self.scan_start] - for ii in range(self.scan_steps): + for ii in range(self.scan_steps+1): self.positions.append(self.scan_start + ii*self.scan_stepsize) def stage(self): @@ -359,24 +366,25 @@ class SnapAndStepScanBase(TemplatedScanBase): # Synthetic values self.scan_stepsize = (scan_end-scan_start)/steps - self.scan_ntotal = steps * burst_at_each_point + self.scan_ntotal = (steps+1) * burst_at_each_point + + # Aerotech DDC settings: Internal event trigger: PsoEvent = 1 + t_modes = {'pso': 0, 'event': 1, 'inp0': 2, 'inp1': 4} + p_modes = {'pso': 'PsoOutput', 'event': 'PsoEvent', 'inp0': 'HighSpeedInput0RisingEdge', 'inp1': 'HighSpeedInput1RisingEdge'} + daq_trigger = t_modes[sync] + pso_trigger = p_modes[sync] filename = "AerotechSnapAndStepTemplate.ascript" filesubs = { 'startpos': self.scan_start, 'stepsize': self.scan_stepsize, 'numsteps': self.scan_steps, - 'exptime': self.exp_time*self.exp_burst - 'settling': self.settling_time + 'exptime': 2*self.exp_time*self.exp_burst, + 'settling': self.settling_time, + 'psotrigger': pso_trigger, + 'npoints': self.scan_ntotal } - - # Aerotech DDC settings: Internal event trigger: PsoEvent = 1 - if isinstance(sync, str): - t_modes = {'pso': 0, 'event': 1, 'inp0': 2, 'inp1': 4} - daq_trigger = t_modes[sync] - else: - daq_trigger = int(sync) - + # DDC config daqname = "es1_ddaq" daqcfg = {'ntotal': self.scan_ntotal, 'trigger': daq_trigger} # Gigafrost config diff --git a/tomcat_bec/scripts/demoscans.py b/tomcat_bec/scripts/demoscans.py index 333b5ef..46988a1 100644 --- a/tomcat_bec/scripts/demoscans.py +++ b/tomcat_bec/scripts/demoscans.py @@ -3,11 +3,13 @@ def bl_check_beam(): """ Checks beamline status""" - return True + motor_enabled = bool(dev.es1_roty.motor_enable.get()) + result = motor_enabled + return result -def demostepscan(scan_start, scan_end, steps, exp_time=0.005, burst_at_each_point=1, settling_time=0, sync='event'): +def demostepscan(scan_start, scan_end, steps, exp_time=0.005, burst_at_each_point=1, settling_time=0, roix=2016, roiy=2016, sync='event'): """ Demo step scan with GigaFrost This is a small BEC user-space demo step scan at the microXAS testbench @@ -30,7 +32,9 @@ def demostepscan(scan_start, scan_end, steps, exp_time=0.005, burst_at_each_poin # Configure gigafrost cfg = { "ntotal": scan_ntotal, "nimages": burst_at_each_point, - "exposure": 1000*exp_time, "period": 2000*exp_time} + "exposure": 1000*exp_time, "period": 2000*exp_time, + "pixel_width": roix, "pixel_height": roiy + } dev.gfclient.configure(d=cfg) # Configure PSO trigger (trigger is normally disabled in fly mode) dev.es1_psod.configure(d={}) @@ -51,7 +55,7 @@ def demostepscan(scan_start, scan_end, steps, exp_time=0.005, burst_at_each_poin def demosequencescan(scan_start, gate_high, gate_low, repeats=1, repmode="PosNeg", - exp_time=0.005, exp_frames=180, sync='pso'): + exp_time=0.005, exp_frames=180, roix=2016, roiy=2016, sync='pso'): """ Demo sequence scan with GigaFrost This is a small BEC user-space sequence scan at the microXAS testbench @@ -67,19 +71,16 @@ def demosequencescan(scan_start, gate_high, gate_low, repeats=1, repmode="PosNeg if not bl_check_beam(): raise RuntimeError("Beamline is not in ready state") - print("Handing over to 'scans.line_scan'") - scans.sequencescan(scan_start, gate_high, gate_low, exp_time=exp_time, exp_frames=exp_frames, repeats=repeats, repmode=repmode) + print("Handing over to 'scans.sequencescan'") + scans.sequencescan(scan_start, gate_high, gate_low, exp_time=exp_time, exp_frames=exp_frames, repeats=repeats, roix=roix, roiy=roiy, repmode=repmode, sync=sync) 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 + exp_time=0.005, exp_frames=180, roix=2016, roiy=2016, sync='pso'): + """ Demo sequence scan with GigaFrost via BEC This is a small BEC user-space sequence scan at the microXAS testbench - triggering a customized scripted scan on the controller. The scan uses - a pre-written custom low-level sequence scan, so it is really minimal. - - NOTE: It calls the AeroScript template version. + doing every scan logic from BEC, i.e. there's no AeroScript involved. Example: -------- @@ -93,8 +94,8 @@ def becsequencescan(start, gate_high, gate_low, repeats=1, repmode="PosNeg", raise RuntimeError(f"Requires at least one frame per cycle, got {self.exp_frames}") # Synthetic values - scan_roix = 2016 - scan_roiy = 2016 + scan_roix = roix + scan_roiy = roiy scan_repnum = repeats scan_repmode = repmode.upper() scan_velocity = gate_high / (exp_time * exp_frames) @@ -122,18 +123,13 @@ def becsequencescan(start, gate_high, gate_low, repeats=1, repmode="PosNeg", else: raise RuntimeError(f"Unsupported repetition mode: {repmode}") - # Aerotech DDC settings: Internal event trigger: PsoEvent = 1 t_modes = {'pso': 0, 'event': 1, 'inp0': 2, 'inp1': 4} - p_modes = {'pso': 'PsoOutput', 'event': 'PsoEvent', 'inp0': 'HighSpeedInput0RisingEdge', 'inp1': 'HighSpeedInput1RisingEdge'} daq_trigger = t_modes[sync] - pso_trigger = p_modes[sync] # Drice data collection config - daqname = "es1_ddaq" daqcfg = {'ntotal': scan_ntotal, 'trigger': daq_trigger} # Gigafrost config - camname = "gfclient" camcfg = { "ntotal": scan_ntotal, "nimages": exp_frames, @@ -143,6 +139,7 @@ def becsequencescan(start, gate_high, gate_low, repeats=1, repmode="PosNeg", "pixel_height": scan_roiy } + # PSO config # Fill PSO vectors according to scan direction # NOTE: Distance counter is bidirectional psoBoundsPos = [] @@ -154,12 +151,7 @@ def becsequencescan(start, gate_high, gate_low, repeats=1, repmode="PosNeg", if scan_repmode in ("POSNEG", "NEGPOS"): psoBoundsPos = [scan_accdistance, gate_high] psoBoundsNeg = [scan_accdistance + gate_low, gate_high] - - # PSO config - psocfg = { - "distance": psoBoundsPos, - "wmode": "toggle" - } + psocfg = {"distance": psoBoundsPos, "wmode": "toggle"} # Move to start position dev.es1_roty.move(scan_start).wait() @@ -206,5 +198,3 @@ def becsequencescan(start, gate_high, gate_low, repeats=1, repmode="PosNeg", # Move back to start dev.es1_roty.move(scan_start).wait() - -