diff --git a/tomcat_bec/devices/aerotech/test/autoHilTest.py b/tomcat_bec/devices/aerotech/test/autoHilTest.py index d976d0f..33b3fde 100644 --- a/tomcat_bec/devices/aerotech/test/autoHilTest.py +++ b/tomcat_bec/devices/aerotech/test/autoHilTest.py @@ -38,7 +38,7 @@ from tomcat_bec.devices.aerotech.AerotechAutomation1Enums import ( os.environ["EPICS_CA_ADDR_LIST"] = "129.129.106.255" AA1_IOC_NAME = "X02DA-ES1-SMP1" AA1_AXIS_NAME = "ROTY" - +MOTOR_TOL = 1.0 class AerotechMotor(EpicsMotor): """ EpicsMotor extension with additional PVs""" @@ -56,6 +56,9 @@ class AerotechAutomation1Test(unittest.TestCase): # HIL testing only if there's hardware dev = aa1Controller(AA1_IOC_NAME + ":CTRL:", name="aa1") dev.wait_for_connection() + mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="mot") + mot.wait_for_connection() + mot.motor_enable.set(1, settle_time=0.1).wait() cls._isConnected = True def test_01_ControllerProxy(self): @@ -294,7 +297,7 @@ class AerotechAutomation1Test(unittest.TestCase): print(f"Absolute move to {target}, distance: {dist}") mot.move(target, wait=True) self.assertTrue( - np.abs(target - mot.position) < 0.1, + np.abs(target - mot.position) < MOTOR_TOL, f"Final position {mot.position} is too far from target {target}", ) @@ -797,7 +800,7 @@ class AerotechAutomation1Test(unittest.TestCase): for ii in range(len(positions)): mot.move(positions[ii]) self.assertTrue( - np.abs(mot.position - positions[ii]) < 1.0, + np.abs(mot.position - positions[ii]) < MOTOR_TOL, f"Expected to reach {positions[ii]}, now is at {mot.position} on step {ii}", ) pso.trigger() @@ -894,12 +897,12 @@ class AerotechAutomation1Test(unittest.TestCase): if scan_type in ["Pos", "Neg"]: mot.move(pos_end) self.assertTrue( - np.abs(mot.position - pos_end) < 1.0, + np.abs(mot.position - pos_end) < MOTOR_TOL, f"Expected to reach {pos_end}, now is at {mot.position} on iteration {ii}", ) mot.move(pos_start) self.assertTrue( - np.abs(mot.position - pos_start) < 1.0, + np.abs(mot.position - pos_start) < MOTOR_TOL, f"Expected to rewind to {pos_start}, now at {mot.position} on iteration {ii}", ) @@ -907,13 +910,13 @@ class AerotechAutomation1Test(unittest.TestCase): if ii % 2 == 0: mot.move(pos_end, wait=True) self.assertTrue( - np.abs(mot.position - pos_end) < 1.0, + np.abs(mot.position - pos_end) < MOTOR_TOL, f"Expected to reach {pos_end}, now at {mot.position} on iteration {ii}", ) else: mot.move(pos_start, wait=True) self.assertTrue( - np.abs(mot.position - pos_start) < 1.0, + np.abs(mot.position - pos_start) < MOTOR_TOL, f"Expected to reach {pos_start}, now at {mot.position} on iteration {ii}", ) @@ -955,14 +958,19 @@ class AerotechAutomation1Test(unittest.TestCase): "startpos": 42, "scanrange": 180, "nrepeat": 13, - "scandir": "NegPos", + "scandir": "NEGPOS", + "npoints": 1300, "scanvel": 150, "scanacc": 500, + "accdist": 0.5*150*150/500, + "psotrigger": "PsoEvent", + "psoBoundsPos": [0.5*150*150/500, 180], + "psoBoundsNeg": [0.5*150*150/500, 180] } tm = jinja2.Template(templatetext) text = tm.render(scan=scanconfig) - # with open("test/tcatSequenceScan.ascript", "w") as text_file: - # text_file.write(text) + with open("./tcatSequenceScanRBV.ascript", "w") as text_file: + text_file.write(text) # Throws if IOC is not available or PVs are incorrect tsk = aa1Tasks(AA1_IOC_NAME + ":TASK:", name="tsk") @@ -987,7 +995,7 @@ class AerotechAutomation1Test(unittest.TestCase): time.sleep(1.0) npoints_rbv = ddc.nsamples_rbv.value self.assertEqual( - npoints_rbv, 13, f"Expected to record 13 DDC points, got: {npoints_rbv} after script" + npoints_rbv, 26, f"Expected to record 13 DDC points, got: {npoints_rbv} after script" ) diff --git a/tomcat_bec/devices/aerotech/test/testSequenceScanTemplate.ascript b/tomcat_bec/devices/aerotech/test/testSequenceScanTemplate.ascript index c34b9ee..9d87cf9 100644 --- a/tomcat_bec/devices/aerotech/test/testSequenceScanTemplate.ascript +++ b/tomcat_bec/devices/aerotech/test/testSequenceScanTemplate.ascript @@ -1,13 +1,18 @@ -// Test program for zig-zag line scanning with PSO window output "enable" -// signal and DDC synchronized to external trigger input. +// Test program for simple zig-zag line scanning with PSO window output +// "enable" signal and DDC synchronized to external trigger input. // The file expects external parameter validation // The PSO locations arrays are set externally from EPICS PV +// +#define DDC_ADDR 0x800000 +#define PSO_ADDR 0x0 + + enum ScanType - Pos = 0 - Neg = 1 - PosNeg = 2 - NegPos = 3 + POS = 0 + NEG = 1 + POSNEG = 2 + NEGPOS = 3 end @@ -15,74 +20,98 @@ program ////////////////////////////////////////////////////////////////////////// // External parameters - USE THEESE var $fStartPosition as real = {{ scan.startpos }} - var $fScanRange as real = $fStartPosition + {{ scan.scanrange }} + var $fScanRange as real = {{ scan.scanrange }} var $iNumRepeat as integer = {{ scan.nrepeat }} - var $eScanType as ScanType = ScanType.{{ scan.scandir or 'Pos' }} + var $eScanType as ScanType = ScanType.{{ scan.scandir or 'POS' }} + var $iNumDdcRead as integer = {{ scan.npoints }} var $fVelJog as real = {{ scan.jogvel or 200 }} var $fVelScan as real = {{ scan.scanvel }} - var $fAcceleration = {{ scan.scanacc or 500 }} - var $fSafeDist = 10.0 - + var $fAcceleration = {{ scan.scanacc }} + var $fAccDistance as real = {{ scan.accdist }} + var $eDdcTrigger as DriveDataCaptureTrigger = DriveDataCaptureTrigger.{{ scan.psotrigger }} + ////////////////////////////////////////////////////////////////////////// // Internal parameters - dont use var $axis as axis = ROTY - var $ii as integer + var $ii as integer + var $axisFaults as integer = 0 var $iDdcSafeSpace as integer = 4096 // Set acceleration SetupAxisRampType($axis, RampType.Linear) - SetupAxisRampValue($axis,0,$fAcceleration) - var $fAccDistance as real = 0.5 * $fVelScan * $fVelScan / $fAcceleration + $fSafeDist + SetupAxisRampValue($axis, 0, $fAcceleration) // set the actual scan range var $fPosStart as real var $fPosEnd as real - if $eScanType == ScanType.Pos + if $eScanType == ScanType.POS $fPosStart = $fStartPosition - $fAccDistance $fPosEnd = $fStartPosition + $fScanRange + $fAccDistance - elseif $eScanType == ScanType.Neg + elseif $eScanType == ScanType.NEG $fPosStart = $fStartPosition + $fAccDistance $fPosEnd = $fStartPosition - $fScanRange - $fAccDistance - elseif $eScanType == ScanType.PosNeg + elseif $eScanType == ScanType.POSNEG $fPosStart = $fStartPosition - $fAccDistance $fPosEnd = $fStartPosition + $fScanRange + $fAccDistance - elseif $eScanType == ScanType.NegPos + elseif $eScanType == ScanType.NEGPOS $fPosStart = $fStartPosition + $fAccDistance $fPosEnd = $fStartPosition - $fScanRange - $fAccDistance end // Move to start position before the scan + // NOTE: Also wait for GigaFrost to start, otherwise early triggers might be missed MoveAbsolute($axis, $fPosStart, $fVelJog) WaitForInPosition($axis) - + Dwell(2) + + // Set globals for feedback + $rglobal[2] = $fPosStart + $rglobal[3] = $fPosEnd + // Configure PSO + // FIXME : When the controller is restarted + PsoDistanceConfigureInputs($axis, [PsoDistanceInput.XC4PrimaryFeedback]) PsoDistanceCounterOff($axis) PsoDistanceEventsOff($axis) PsoWindowConfigureEvents($axis, PsoWindowEventMode.None) PsoWaveformOff($axis) - PsoOutputConfigureSource($axis, PsoOutputSource.Waveform) - var $iPsoArrayAddr as integer = 0 - var $iPsoArray[] as real = [UnitsToCounts($axis, $fAccDistance), UnitsToCounts($axis, $fScanRange)] - DriveArrayWrite($axis, $iPsoArray, $iPsoArrayAddr, length($iPsoArray), DriveArrayType.PsoDistanceEventDistances) - PsoDistanceConfigureArrayDistances($axis, $iPsoArrayAddr, 2, 0) + // Simple PSO trigger pattern + var $iPsoArrayPosAddr as integer = PSO_ADDR + var $iPsoArrayPos[] as real = [{% for psoDist in scan.psoBoundsPos[:-1] %} UnitsToCounts($axis, {{ psoDist }}), {% endfor %} UnitsToCounts($axis, {{ scan.psoBoundsPos[-1] }}) ] + DriveArrayWrite($axis, $iPsoArrayPos, $iPsoArrayPosAddr, length($iPsoArrayPos), DriveArrayType.PsoDistanceEventDistances) + + {% if scan.psoBoundsNeg|length > 0 %} + var $iPsoArrayNegAddr as integer = ($iPsoArrayPosAddr + length($iPsoArrayPos)) * 4 + var $iPsoArrayNeg[] as real = [{% for psoDist in scan.psoBoundsNeg[:-1] %} UnitsToCounts($axis, {{ psoDist }}), {% endfor %} UnitsToCounts($axis, {{ scan.psoBoundsNeg[-1] }}) ] + DriveArrayWrite($axis, $iPsoArrayNeg, $iPsoArrayNegAddr, length($iPsoArrayNeg), DriveArrayType.PsoDistanceEventDistances) + {% else %} + var $iPsoArrayNegAddr as integer = ($iPsoArrayPosAddr + length($iPsoArrayPos)) * 4 + var $iPsoArrayNegSize as integer = 0 + var $iPsoArrayNeg[] as real = [] + // DriveArrayWrite($axis, $iPsoArrayNeg, $iPsoArrayNegAddr, length($iPsoArrayNeg), DriveArrayType.PsoDistanceEventDistances) + {% endif %} + + + PsoDistanceConfigureArrayDistances($axis, $iPsoArrayPosAddr, length($iPsoArrayPos), 0) + PsoDistanceCounterOn($axis) PsoDistanceEventsOn($axis) PsoWaveformConfigureMode($axis, PsoWaveformMode.Toggle) - PsoWaveformOn($axis) + PsoWaveformOn($axis) + PsoOutputConfigureSource($axis, PsoOutputSource.Waveform) // Configure Drive Data Collection - var $iDdcArrayAddr as integer = 8388608 - var $iDdcArraySize as integer = 5000 + var $iDdcArraySize as integer = $iNumDdcRead 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); + DriveDataCaptureConfigureArray($axis, 0, DDC_ADDR, $iDdcArraySize); + DriveDataCaptureConfigureArray($axis, 1, DDC_ADDR + $iDdcSafeSpace + 8 * $iDdcArraySize, $iDdcArraySize); // Directly before scan PsoDistanceCounterOn($axis) @@ -92,41 +121,44 @@ program /////////////////////////////////////////////////////////// // Start the actual scanning /////////////////////////////////////////////////////////// - for $ii = 0 to ($iNumRepeat-1) - PsoDistanceConfigureArrayDistances($axis, $iPsoArrayAddr, 2, 0) - if $eScanType == ScanType.Pos - MoveAbsolute($axis, $fPosEnd, $fVelScan) - WaitForInPosition($axis) - MoveAbsolute($axis, $fPosStart, $fVelScan) - WaitForInPosition($axis) - elseif $eScanType == ScanType.Neg - MoveAbsolute($axis, $fPosEnd, $fVelScan) - WaitForInPosition($axis) - MoveAbsolute($axis, $fPosStart, $fVelScan) - WaitForInPosition($axis) - elseif $eScanType == ScanType.PosNeg - if ($ii % 2) == 0 - MoveAbsolute($axis, $fPosEnd, $fVelScan) - WaitForInPosition($axis) - elseif ($ii % 2) == 1 - MoveAbsolute($axis, $fPosStart, $fVelScan) - WaitForInPosition($axis) - end - elseif $eScanType == ScanType.NegPos - if ($ii % 2) == 0 - MoveAbsolute($axis, $fPosEnd, $fVelScan) - WaitForInPosition($axis) - elseif ($ii % 2) == 1 - MoveAbsolute($axis, $fPosStart, $fVelScan) - WaitForInPosition($axis) - end + if $eScanType == ScanType.POS || $eScanType == ScanType.NEG + PsoDistanceConfigureArrayDistances($axis, $iPsoArrayPosAddr, length($iPsoArrayPos), 0) + MoveAbsolute($axis, $fPosEnd, $fVelScan) + WaitForMotionDone($axis) + + $axisFaults = StatusGetAxisItem($axis, AxisDataSignal.AxisFault) + if $axisFaults + TaskSetError(TaskGetIndex(), "AxisFault on axis ROTY") + end + elseif $eScanType == ScanType.POSNEG || $eScanType == ScanType.NEGPOS + for $ii = 0 to ($iNumRepeat-1) + // Feedback on progress + $rglobal[4] = $ii + if ($ii % 2) == 0 + PsoDistanceConfigureArrayDistances($axis, $iPsoArrayPosAddr, length($iPsoArrayPos), 0) + MoveAbsolute($axis, $fPosEnd, $fVelScan) + elseif ($ii % 2) == 1 + PsoDistanceConfigureArrayDistances($axis, $iPsoArrayNegAddr, length($iPsoArrayNeg), 0) + MoveAbsolute($axis, $fPosStart, $fVelScan) + end + WaitForMotionDone($axis) + + $axisFaults = StatusGetAxisItem($axis, AxisDataSignal.AxisFault) + if $axisFaults + TaskSetError(TaskGetIndex(), "AxisFault on axis ROTY") + end + Dwell(0.2) end - Dwell(0.2) end // Directly after scan PsoDistanceCounterOff($axis) DriveDataCaptureOff($axis, 0) DriveDataCaptureOff($axis, 1) + // move back to start position + MoveAbsolute($axis, $fPosStart, $fVelJog) + WaitForInPosition($axis) + Dwell(2) end + diff --git a/tomcat_bec/tcatSequenceScanRBV.ascript b/tomcat_bec/tcatSequenceScanRBV.ascript new file mode 100644 index 0000000..363b416 --- /dev/null +++ b/tomcat_bec/tcatSequenceScanRBV.ascript @@ -0,0 +1,158 @@ +// Test program for simple zig-zag line scanning with PSO window output +// "enable" signal and DDC synchronized to external trigger input. +// The file expects external parameter validation +// The PSO locations arrays are set externally from EPICS PV +// +#define DDC_ADDR 0x800000 +#define PSO_ADDR 0x0 + + + +enum ScanType + POS = 0 + NEG = 1 + POSNEG = 2 + NEGPOS = 3 +end + + +program + ////////////////////////////////////////////////////////////////////////// + // External parameters - USE THEESE + var $fStartPosition as real = 42 + var $fScanRange as real = 180 + var $iNumRepeat as integer = 13 + var $eScanType as ScanType = ScanType.NEGPOS + var $iNumDdcRead as integer = 1300 + + var $fVelJog as real = 200 + var $fVelScan as real = 150 + var $fAcceleration = 500 + var $fAccDistance as real = 22.5 + var $eDdcTrigger as DriveDataCaptureTrigger = DriveDataCaptureTrigger.PsoEvent + + ////////////////////////////////////////////////////////////////////////// + // Internal parameters - dont use + var $axis as axis = ROTY + var $ii as integer + var $axisFaults as integer = 0 + var $iDdcSafeSpace as integer = 4096 + + // Set acceleration + SetupAxisRampType($axis, RampType.Linear) + SetupAxisRampValue($axis, 0, $fAcceleration) + + // set the actual scan range + var $fPosStart as real + var $fPosEnd as real + if $eScanType == ScanType.POS + $fPosStart = $fStartPosition - $fAccDistance + $fPosEnd = $fStartPosition + $fScanRange + $fAccDistance + elseif $eScanType == ScanType.NEG + $fPosStart = $fStartPosition + $fAccDistance + $fPosEnd = $fStartPosition - $fScanRange - $fAccDistance + elseif $eScanType == ScanType.POSNEG + $fPosStart = $fStartPosition - $fAccDistance + $fPosEnd = $fStartPosition + $fScanRange + $fAccDistance + elseif $eScanType == ScanType.NEGPOS + $fPosStart = $fStartPosition + $fAccDistance + $fPosEnd = $fStartPosition - $fScanRange - $fAccDistance + end + + // Move to start position before the scan + // NOTE: Also wait for GigaFrost to start, otherwise early triggers might be missed + MoveAbsolute($axis, $fPosStart, $fVelJog) + WaitForInPosition($axis) + Dwell(2) + + // Set globals for feedback + $rglobal[2] = $fPosStart + $rglobal[3] = $fPosEnd + + // Configure PSO + // FIXME : When the controller is restarted + PsoDistanceConfigureInputs($axis, [PsoDistanceInput.XC4PrimaryFeedback]) + PsoDistanceCounterOff($axis) + PsoDistanceEventsOff($axis) + PsoWindowConfigureEvents($axis, PsoWindowEventMode.None) + PsoWaveformOff($axis) + // Simple PSO trigger pattern + var $iPsoArrayPosAddr as integer = PSO_ADDR + var $iPsoArrayPos[] as real = [ UnitsToCounts($axis, 22.5), UnitsToCounts($axis, 180) ] + DriveArrayWrite($axis, $iPsoArrayPos, $iPsoArrayPosAddr, length($iPsoArrayPos), DriveArrayType.PsoDistanceEventDistances) + + + var $iPsoArrayNegAddr as integer = ($iPsoArrayPosAddr + length($iPsoArrayPos)) * 4 + var $iPsoArrayNeg[] as real = [ UnitsToCounts($axis, 22.5), UnitsToCounts($axis, 180) ] + DriveArrayWrite($axis, $iPsoArrayNeg, $iPsoArrayNegAddr, length($iPsoArrayNeg), DriveArrayType.PsoDistanceEventDistances) + + + + PsoDistanceConfigureArrayDistances($axis, $iPsoArrayPosAddr, length($iPsoArrayPos), 0) + PsoDistanceCounterOn($axis) + PsoDistanceEventsOn($axis) + + PsoWaveformConfigureMode($axis, PsoWaveformMode.Toggle) + PsoWaveformOn($axis) + PsoOutputConfigureSource($axis, PsoOutputSource.Waveform) + + // Configure Drive Data Collection + var $iDdcArraySize as integer = $iNumDdcRead + + DriveDataCaptureConfigureInput($axis, 0, DriveDataCaptureInput.PrimaryFeedback); + DriveDataCaptureConfigureInput($axis, 1, DriveDataCaptureInput.AnalogInput0 ); + + DriveDataCaptureConfigureTrigger($axis, 0, $eDdcTrigger ); + DriveDataCaptureConfigureTrigger($axis, 1, $eDdcTrigger ); + + DriveDataCaptureConfigureArray($axis, 0, DDC_ADDR, $iDdcArraySize); + DriveDataCaptureConfigureArray($axis, 1, DDC_ADDR + $iDdcSafeSpace + 8 * $iDdcArraySize, $iDdcArraySize); + + // Directly before scan + PsoDistanceCounterOn($axis) + DriveDataCaptureOn($axis, 0) + DriveDataCaptureOn($axis, 1) + + /////////////////////////////////////////////////////////// + // Start the actual scanning + /////////////////////////////////////////////////////////// + + if $eScanType == ScanType.POS || $eScanType == ScanType.NEG + PsoDistanceConfigureArrayDistances($axis, $iPsoArrayPosAddr, length($iPsoArrayPos), 0) + MoveAbsolute($axis, $fPosEnd, $fVelScan) + WaitForMotionDone($axis) + + $axisFaults = StatusGetAxisItem($axis, AxisDataSignal.AxisFault) + if $axisFaults + TaskSetError(TaskGetIndex(), "AxisFault on axis ROTY") + end + elseif $eScanType == ScanType.POSNEG || $eScanType == ScanType.NEGPOS + for $ii = 0 to ($iNumRepeat-1) + // Feedback on progress + $rglobal[4] = $ii + if ($ii % 2) == 0 + PsoDistanceConfigureArrayDistances($axis, $iPsoArrayPosAddr, length($iPsoArrayPos), 0) + MoveAbsolute($axis, $fPosEnd, $fVelScan) + elseif ($ii % 2) == 1 + PsoDistanceConfigureArrayDistances($axis, $iPsoArrayNegAddr, length($iPsoArrayNeg), 0) + MoveAbsolute($axis, $fPosStart, $fVelScan) + end + WaitForMotionDone($axis) + + $axisFaults = StatusGetAxisItem($axis, AxisDataSignal.AxisFault) + if $axisFaults + TaskSetError(TaskGetIndex(), "AxisFault on axis ROTY") + end + Dwell(0.2) + end + end + + // Directly after scan + PsoDistanceCounterOff($axis) + DriveDataCaptureOff($axis, 0) + DriveDataCaptureOff($axis, 1) + // move back to start position + MoveAbsolute($axis, $fPosStart, $fVelJog) + WaitForInPosition($axis) + Dwell(2) +end