diff --git a/ophyd_devices/epics/devices/AerotechAutomation1.py b/ophyd_devices/epics/devices/AerotechAutomation1.py index c21ff3b..fc14f26 100644 --- a/ophyd_devices/epics/devices/AerotechAutomation1.py +++ b/ophyd_devices/epics/devices/AerotechAutomation1.py @@ -119,6 +119,7 @@ class aa1Tasks(Device): it and run it directly on a certain thread. But there's no way to retrieve the source code. """ + SUB_PROGRESS = "progress" _failure = Component(EpicsSignalRO, "FAILURE", kind=Kind.hinted) errStatus = Component(EpicsSignalRO, "ERRW", auto_monitor=True, kind=Kind.hinted) warnStatus = Component(EpicsSignalRO, "WARNW", auto_monitor=True, kind=Kind.hinted) @@ -139,7 +140,23 @@ class aa1Tasks(Device): self._textToExecute = None self._isConfigured = False self._isStepConfig = False + self.subscribe(self._progress_update, "progress", run=False) + def _progress_update(self, value, **kwargs) -> None: + """Progress update on the scan""" + value = self.progress() + self._run_subs( sub_type=self.SUB_PROGRESS, value=value, max_value=1, done=1, ) + + def _progress(self) -> None: + """Progress update on the scan""" + if self._currentTaskMonitor is None: + return 1 + else: + if self._currentTaskMonitor.status.value in ["Running", 4]: + return 0 + else: + return 1 + def readFile(self, filename: str) -> str: """ Read a file from the controller """ # Have to use CHAR array due to EPICS LSI bug... @@ -198,7 +215,8 @@ class aa1Tasks(Device): old = self.read_configuration() self.taskIndex.set(taskIndex).wait() self._textToExecute = None - #self._currentTaskMonitor = aa1TaskState() + self._currentTaskMonitor = aa1TaskState(prefix=self.prefix+f"T{taskIndex}:", name="taskmon") + self._currentTaskMonitor.wait_for_connection() # Choose the right execution mode if (filename is None) and (text not in [None, ""]): @@ -270,11 +288,22 @@ class aa1Tasks(Device): def complete(self) -> DeviceStatus: """ Execute the script on the configured task""" + print("Called aa1Task.complete()") #return self._currentTaskMonitor.complete() - status = DeviceStatus(self) - status.set_finished() + #status = DeviceStatus(self) + #status.set_finished() + #return status + timestamp_ = 0 + def notRunning(*args, old_value, value, timestamp, **kwargs): + nonlocal timestamp_ + result = False if (timestamp_== 0) else (value not in ["Running", 4]) + timestamp_ = timestamp + print(result) + return result + # Subscribe and wait for update + status = SubscriptionStatus(self._currentTaskMonitor.status, notRunning, settle_time=0.5) return status - + def describe_collect(self) -> OrderedDict: dd = OrderedDict() dd['success'] = {'source': "internal", 'dtype': 'integer', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} @@ -300,12 +329,14 @@ class aa1TaskState(Device): def complete(self) -> StatusBase: """ Bluesky flyer interface""" + print("Called aa1TaskState.complete()") # Define wait until the busy flag goes down (excluding initial update) timestamp_ = 0 def notRunning(*args, old_value, value, timestamp, **kwargs): nonlocal timestamp_ result = False if (timestamp_== 0) else (value not in ["Running", 4]) timestamp_ = timestamp + print(result) return result # Subscribe and wait for update diff --git a/ophyd_devices/epics/devices/AerotechSnapAndStepTemplate.ascript b/ophyd_devices/epics/devices/AerotechSnapAndStepTemplate.ascript new file mode 100644 index 0000000..c7e01c8 --- /dev/null +++ b/ophyd_devices/epics/devices/AerotechSnapAndStepTemplate.ascript @@ -0,0 +1,87 @@ +// Snap-and-step +// Test program for high speed step scanning with individual triggers on PSO output. +// The file expects external parameter validation. + + + +program + // External parameters + var $fStartPosition as real = {{ scan.startpos }} + var $iNumSteps as integer = {{ scan.numsteps }} + var $fStepSize as real = {{ scan.stepsize }} + var $fExposureTimeSec as real = {{ scan.exptime }} + var $fVelJog as real = {{ scan.travel or 200 }} + var $fVelScan as real = {{ scan.velocity or 50 }} + var $fAcceleration = {{ scan.acceleration or 500 }} + + // Internal + var $axis as axis = ROTY + var $ii as integer + + // Set acceleration + SetupAxisRampType($axis, RampType.Linear) + SetupAxisRampValue($axis,0,$fAcceleration) + + // Move to start position before the scan + var $fPosNext as real = $fStartPosition + MoveAbsolute($axis, $fPosNext, $fVelJog) + WaitForInPosition($axis) + + // Configure PSO (to manual event generation) + PsoDistanceEventsOff($axis) + PsoWindowConfigureEvents($axis, PsoWindowEventMode.None) + PsoWaveformConfigurePulseFixedTotalTime($axis, 50) + PsoWaveformConfigurePulseFixedOnTime($axis, 20) + PsoWaveformConfigurePulseFixedCount($axis, 1) + PsoWaveformApplyPulseConfiguration($axis) + PsoWaveformConfigureMode($axis, PsoWaveformMode.Pulse) + PsoOutputConfigureSource($axis, PsoOutputSource.Waveform) + PsoWaveformOn($axis) + + // Configure Drive Data Collection + var $iDdcArrayAddr as integer = 8388608 + var $iDdcArraySize as integer = iMaximum(5000, $iNumSteps) + 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 ); + + DriveDataCaptureConfigureArray($axis, 0, $iDdcArrayAddr, $iDdcArraySize); + DriveDataCaptureConfigureArray($axis, 1, $iDdcArrayAddr + $iDdcSafeSpace + 8 * $iDdcArraySize, $iDdcArraySize); + + // Directly before scan + PsoDistanceCounterOn($axis) + DriveDataCaptureOn($axis, 0) + DriveDataCaptureOn($axis, 1) + + /////////////////////////////////////////////////////////// + // Start the actual scanning + /////////////////////////////////////////////////////////// + for $ii = 0 to ($iNumSteps-1) + MoveAbsolute($axis, $fPosNext, $fVelScan) + WaitForInPosition($axis) + PsoEventGenerateSingle($axis) + Dwell($fExposureTimeSec) + $fPosNext = $fPosNext + $fStepSize + end + + // Directly after scan + PsoWaveformOff($axis) + DriveDataCaptureOff($axis, 0) + DriveDataCaptureOff($axis, 1) +end + +// Demonstrates using a switch/case conditional. +function iMaximum($A as integer, $B as integer) as integer + var $retVal + if ($A > $B) + $retVal = $A + else + $retVal = $B + end + return $retVal +end +