Test fixes

This commit is contained in:
gac-x02da
2025-06-18 18:14:24 +02:00
committed by marone
parent 9aa4db3b3b
commit 8043785885
6 changed files with 80 additions and 161 deletions
+1 -1
View File
@@ -280,7 +280,7 @@ class aa1AxisPsoDistance(AerotechPsoBase):
"""Fire a single PSO event (i.e. manual software trigger)""" """Fire a single PSO event (i.e. manual software trigger)"""
# Only trigger if distance was set to invalid # Only trigger if distance was set to invalid
# if self.dstDistanceVal.get() == 0: # if self.dstDistanceVal.get() == 0:
logger.warning(f"[{self.name}] Triggerin...") # logger.warning(f"[{self.name}] Triggerin...")
return self.fire(settle_time) return self.fire(settle_time)
def arm(self) -> None: def arm(self) -> None:
@@ -33,7 +33,7 @@ program
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Internal parameters - dont use // Internal parameters - dont use
var $axis as axis = ROTY var $axis as axis = {{ scan.rotaxis or 'ROTY' }}
var $ii as integer var $ii as integer
var $axisFaults as integer = 0 var $axisFaults as integer = 0
var $iDdcSafeSpace as integer = 4096 var $iDdcSafeSpace as integer = 4096
@@ -20,7 +20,7 @@ program
// Internal // Internal
var $axis as axis = ROTY var $axis as axis = {{ scan.rotaxis or 'ROTY' }}
var $ii as integer var $ii as integer
var $axisFaults as integer = 0 var $axisFaults as integer = 0
+4
View File
@@ -7,3 +7,7 @@ from .AerotechAutomation1 import (
aa1GlobalVariableBindings, aa1GlobalVariableBindings,
aa1AxisIo, aa1AxisIo,
) )
from .AerotechAutomation1Enums import (
DriveDataCaptureInput,
DriveDataCaptureTrigger,
)
+72 -157
View File
@@ -16,7 +16,7 @@ import time
import os import os
import jinja2 import jinja2
import numpy as np import numpy as np
from ophyd import Component, EpicsMotor, EpicsSignal from ophyd import Component, EpicsMotor, EpicsSignal, EpicsSignalRO
from tomcat_bec.devices.aerotech import ( from tomcat_bec.devices.aerotech import (
@@ -27,21 +27,22 @@ from tomcat_bec.devices.aerotech import (
aa1Tasks, aa1Tasks,
aa1AxisDriveDataCollection, aa1AxisDriveDataCollection,
aa1AxisPsoDistance, aa1AxisPsoDistance,
)
from tomcat_bec.devices.aerotech.AerotechAutomation1Enums import (
DriveDataCaptureInput, DriveDataCaptureInput,
DriveDataCaptureTrigger, DriveDataCaptureTrigger,
) )
os.environ["EPICS_CA_ADDR_LIST"] = "129.129.106.255"
os.environ["EPICS_CA_ADDR_LIST"] = "129.129.106.255 129.129.99.255"
AA1_IOC_NAME = "X02DA-ES1-SMP1" AA1_IOC_NAME = "X02DA-ES1-SMP1"
AA1_AXIS_NAME = "ROTY" AA1_IOC_AXIS = "ROTY"
MOTOR_TOL = 1.0 AA1_DRV_AXIS = "RY"
AXIS_VELOCITY = 42
AXIS_TRAVEL = 80
AXIS_TOL = 1.0
class AerotechMotor(EpicsMotor): class AerotechMotor(EpicsMotor):
""" EpicsMotor extension with additional PVs""" """ EpicsMotor extension with additional PVs"""
motor_enable_rbv = Component(EpicsSignalRO, "-EnaAct", kind="omitted", auto_monitor=True)
motor_enable = Component(EpicsSignal, ".CNEN", kind="omitted", auto_monitor=True) motor_enable = Component(EpicsSignal, ".CNEN", kind="omitted", auto_monitor=True)
@@ -56,7 +57,7 @@ class AerotechAutomation1Test(unittest.TestCase):
# HIL testing only if there's hardware # HIL testing only if there's hardware
dev = aa1Controller(AA1_IOC_NAME + ":CTRL:", name="aa1") dev = aa1Controller(AA1_IOC_NAME + ":CTRL:", name="aa1")
dev.wait_for_connection() dev.wait_for_connection()
mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="mot") mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_IOC_AXIS, name="mot")
mot.wait_for_connection() mot.wait_for_connection()
mot.motor_enable.set(1, settle_time=0.1).wait() mot.motor_enable.set(1, settle_time=0.1).wait()
cls._isConnected = True cls._isConnected = True
@@ -83,7 +84,7 @@ class AerotechAutomation1Test(unittest.TestCase):
print("TASK: Testing the 'run' command (run text with intermediary file)") print("TASK: Testing the 'run' command (run text with intermediary file)")
# Send another empty program # Send another empty program
filename = "unittesting2.ascript" filename = "test_program.ascript"
text = "program\nvar $positions[4] as real = [2, 4, 8, 16]\nend" text = "program\nvar $positions[4] as real = [2, 4, 8, 16]\nend"
dev.configure({'script_file': filename, 'script_task': 3, 'script_text': text}) dev.configure({'script_file': filename, 'script_task': 3, 'script_text': text})
dev.arm() dev.arm()
@@ -120,21 +121,21 @@ class AerotechAutomation1Test(unittest.TestCase):
# # Attempt to run a short motion command using the Execute interface # # Attempt to run a short motion command using the Execute interface
print("TASK: Testing a short motion command") print("TASK: Testing a short motion command")
text = """ text = f"""
var $fDist as real = 90 var $fDist as real = 90
var $fVelo as real = 200 var $fVelo as real = 60
var $axis as axis = ROTY var $axis as axis = {AA1_DRV_AXIS}
Enable($axis) Enable($axis)
MoveLinear($axis, $fDist, $fVelo) MoveLinear($axis, $fDist, $fVelo)
""" """
dev.configure({'script_task': 3, 'script_text': text}) dev.configure({'script_task': 3, 'script_text': text, 'script_file': "test_shortmove.ascript"})
dev.arm() dev.arm()
t_start = time.time() t_start = time.time()
dev.kickoff() dev.kickoff()
dev.complete().wait() dev.complete().wait()
t_end = time.time() t_end = time.time()
t_elapsed = t_end - t_start t_elapsed = t_end - t_start
self.assertTrue( 0 < t_elapsed < 4, f"Motion finished outside of window in: {t_elapsed}") self.assertTrue( 1 < t_elapsed < 4, f"Motion finished outside of window in: {t_elapsed}")
dev.unstage() dev.unstage()
print("TASK: Done testing TASK module!") print("TASK: Done testing TASK module!")
@@ -268,7 +269,7 @@ class AerotechAutomation1Test(unittest.TestCase):
$iglobal[1]={val_i} $iglobal[1]={val_i}
$rglobal[1]={val_f} $rglobal[1]={val_f}
""" """
tsk.configure({'script_text': text, 'script_task': 4}) tsk.configure({'script_text': text, 'script_task': 4, 'script_file': "test_global.ascript"})
tsk.arm() tsk.arm()
tsk.launch() tsk.launch()
tsk.complete().wait() tsk.complete().wait()
@@ -285,19 +286,30 @@ class AerotechAutomation1Test(unittest.TestCase):
def test_05_axis_motorrecord(self): def test_05_axis_motorrecord(self):
"""Tests the basic move functionality of the MotorRecord""" """Tests the basic move functionality of the MotorRecord"""
mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="x") mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_IOC_AXIS, name="x")
mot.wait_for_connection() mot.wait_for_connection()
mot.motor_enable.set(1, settle_time=0.1).wait() mot.motor_enable.set(1, settle_time=0.1).wait()
mot.velocity.set(200).wait() mot.velocity.set(AXIS_VELOCITY).wait()
print("\nMR: Checking the standard EPICS motor record") print("\nMR: Checking the standard EPICS motor record")
# Test enabling/disabling the axis
mot.motor_enable.set(1, settle_time=0.1).wait()
time.sleep(1)
self.assertTrue(mot.motor_enable_rbv.value, "Motor should be enabled")
mot.motor_enable.set(0, settle_time=0.1).wait()
time.sleep(1)
self.assertFalse(mot.motor_enable_rbv.value, "Motor should be disabled")
mot.motor_enable.set(1, settle_time=0.1).wait()
time.sleep(1)
self.assertTrue(mot.motor_enable_rbv.value, "Motor should be enabled")
# Four test with absolute moves # Four test with absolute moves
def move_rel(dist): def move_rel(dist):
target = mot.position + dist target = mot.position + dist
print(f"Absolute move to {target}, distance: {dist}") print(f"Absolute move to {target}, distance: {dist}")
mot.move(target, wait=True) mot.move(target, wait=True)
self.assertTrue( self.assertTrue(
np.abs(target - mot.position) < MOTOR_TOL, np.abs(target - mot.position) < AXIS_TOL,
f"Final position {mot.position} is too far from target {target}", f"Final position {mot.position} is too far from target {target}",
) )
@@ -311,7 +323,7 @@ class AerotechAutomation1Test(unittest.TestCase):
def test_06_axis_io(self): def test_06_axis_io(self):
"""Test axis IO bindings""" """Test axis IO bindings"""
# Throws if IOC is not available or PVs are incorrect # Throws if IOC is not available or PVs are incorrect
dev = aa1AxisIo(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":IO:", name="io") dev = aa1AxisIo(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":IO:", name="io")
dev.wait_for_connection() dev.wait_for_connection()
# Writing random values to analog/digital outputs # Writing random values to analog/digital outputs
@@ -338,9 +350,9 @@ class AerotechAutomation1Test(unittest.TestCase):
configuration, state monitoring and readback. configuration, state monitoring and readback.
""" """
# Throws if IOC is not available or PVs are incorrect # Throws if IOC is not available or PVs are incorrect
dev = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":DDC:", name="ddc") dev = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":DDC:", name="ddc")
event = EpicsSignal( event = EpicsSignal(
AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":PSO:EVENT:SINGLE", put_complete=True AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":PSO:EVENT:SINGLE", put_complete=True
) )
dev.wait_for_connection() dev.wait_for_connection()
event.wait_for_connection() event.wait_for_connection()
@@ -464,9 +476,9 @@ class AerotechAutomation1Test(unittest.TestCase):
the PSO module and the polled feedback. the PSO module and the polled feedback.
""" """
# Throws if IOC is not available or PVs are incorrect # Throws if IOC is not available or PVs are incorrect
pso = aa1AxisPsoDistance(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":PSO:", name="pso1") pso = aa1AxisPsoDistance(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":PSO:", name="pso1")
ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":DDC:", name="ddc") ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":DDC:", name="ddc")
mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name=AA1_AXIS_NAME.lower()) mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_IOC_AXIS, name=AA1_IOC_AXIS.lower())
mot.wait_for_connection() mot.wait_for_connection()
pso.wait_for_connection() pso.wait_for_connection()
ddc.wait_for_connection() ddc.wait_for_connection()
@@ -479,7 +491,7 @@ class AerotechAutomation1Test(unittest.TestCase):
# Configure steps and move to middle of range # Configure steps and move to middle of range
pso.configure({"pso_distance": 5, "pso_wavemode": "toggle"}) pso.configure({"pso_distance": 5, "pso_wavemode": "toggle"})
pso.arm() pso.arm()
mot.velocity.set(200).wait() mot.velocity.set(AXIS_VELOCITY).wait()
mot.move(mot.position + 2.5, wait=True) mot.move(mot.position + 2.5, wait=True)
ddc.arm() ddc.arm()
@@ -527,7 +539,7 @@ class AerotechAutomation1Test(unittest.TestCase):
pso.disarm() pso.disarm()
ddc.disarm() ddc.disarm()
print("AX:PSO: Checking basic 'pulsed' event generation with DDC (3/5)") print("AX:PSO: Checking basic 'pulsed' event generation with DDC (3/3)")
# Configure steps and move to middle of range # Configure steps and move to middle of range
pso.configure({"pso_distance": 1.8, "pso_wavemode": "pulsed", "pso_n_pulse": 3}) pso.configure({"pso_distance": 1.8, "pso_wavemode": "pulsed", "pso_n_pulse": 3})
pso.arm() pso.arm()
@@ -565,9 +577,9 @@ class AerotechAutomation1Test(unittest.TestCase):
and compares them to the expected values. and compares them to the expected values.
""" """
# Throws if IOC is not available or PVs are incorrect # Throws if IOC is not available or PVs are incorrect
pso = aa1AxisPsoDistance(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":PSO:", name="pso1") pso = aa1AxisPsoDistance(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":PSO:", name="pso1")
ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":DDC:", name="ddc") ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":DDC:", name="ddc")
mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="x") mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_IOC_AXIS, name="x")
mot.wait_for_connection() mot.wait_for_connection()
pso.wait_for_connection() pso.wait_for_connection()
ddc.wait_for_connection() ddc.wait_for_connection()
@@ -578,7 +590,7 @@ class AerotechAutomation1Test(unittest.TestCase):
"\ntest_09_PsoIntegration01: Integration test for vector PSO triggering (advanced)" "\ntest_09_PsoIntegration01: Integration test for vector PSO triggering (advanced)"
) )
print("AX:PSO: Testing standard 'enable' style PSO triggering with DDC (1/3)") print("AX:PSO: Testing standard 'enable' style PSO triggering with DDC (1/2)")
# This is one line of the Tomcat sequence scan! # This is one line of the Tomcat sequence scan!
acc_dist = 0.5 * mot.velocity.value * mot.acceleration.value acc_dist = 0.5 * mot.velocity.value * mot.acceleration.value
vec_dist = [acc_dist, 18, 0.01, 18, 0.01, 18, 0.01, 18, 0.01, 18, 0.01, 18] vec_dist = [acc_dist, 18, 0.01, 18, 0.01, 18, 0.01, 18, 0.01, 18, 0.01, 18]
@@ -615,7 +627,7 @@ class AerotechAutomation1Test(unittest.TestCase):
pso.unstage() pso.unstage()
ddc.unstage() ddc.unstage()
print("AX:PSO: Checking 'individual' style trigger signal with DDC (2/5)") print("AX:PSO: Checking 'individual' style trigger signal with DDC (2/2)")
# This is practically golden ratio tomography! # This is practically golden ratio tomography!
acc_dist = 0.5 * mot.velocity.value * mot.acceleration.value acc_dist = 0.5 * mot.velocity.value * mot.acceleration.value
vec_dist = [acc_dist] vec_dist = [acc_dist]
@@ -654,114 +666,16 @@ class AerotechAutomation1Test(unittest.TestCase):
pso.unstage() pso.unstage()
ddc.unstage() ddc.unstage()
# def test_10_AxisPsoWindow1(self): def test_10_StepScan(self):
# """ Test basic PSO window mode functionality
# This module tests basic PSO distance functionality, like events and
# waveform generation. The goal is to ensure the basic operation of
# the PSO module and the polled feedback.
# """
# # Throws if IOC is not available or PVs are incorrect
# pso = aa1AxisPsoWindow(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":PSO:", name="pso1")
# ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":DDC:", name="ddc")
# mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="x")
# mot.wait_for_connection()
# pso.wait_for_connection()
# ddc.wait_for_connection()
# pso.unstage()
# ddc.unstage()
# mot.unstage()
# print("\nAX:PSO: Checking axis PSO in window mode (basic)")
# print("AX:PSO: Directly checking basic window output (1/3)")
# # Configure steps and move to middle of range
# pso.configure({'bounds': (5, 13), 'pso_wavemode': "output"})
# time.sleep(1)
# # Move half a step
# mot.move(mot.position + 0.1)
# dist = 0.1
# # Start moving in steps, while checking output
# for ii in range(42):
# mot.move(mot.position + 0.2)
# dist += 0.2
# time.sleep(1)
# if 5 < dist < 13:
# self.assertTrue(pso.output.value == 1,
# f"Expected HIGH PSO output inside the window at distance {dist}")
# else:
# self.assertTrue(pso.output.value == 0,
# f"Expected LOW PSO output outside the window at distance {dist}")
# print("AX:PSO: The next tests fail due to encoder jitter!")
# pso.unstage()
# return
# print("AX:PSO: Checking basic window event generation with DDC (2/3)")
# # Move to acceleration position
# acc_dist = 0.5 * mot.velocity.value * mot.acceleration.value
# print(f"Acceleration distance: {acc_dist}")
# start_position = mot.position
# mot.move(start_position - acc_dist)
# # Configure PSO in window mode
# cfg = {'bounds': (acc_dist, 30 + acc_dist), 'pso_wavemode': "pulsed", "emode": 'Both'}
# dev.configure(cfg)
# # Configure the data capture (must be performed in start position)
# cfg = {'ntotal': 5*10, 'trigger': DriveDataCaptureTrigger.PsoOutput}
# ddc.configure(cfg)
# ddc.arm()
# npoints_rbv = ddc.nsamples_rbv.value
# self.assertEqual(npoints_rbv, 0,
# f"DDC.stage should reset the number of DDC points but got: {npoints_rbv}")
# # Repeated line scan
# for ii in range(10):
# dev.arm()
# mot.move(start_position + 30 + acc_dist)
# dev.unstage()
# mot.move(start_position - acc_dist)
# time.sleep(0.5)
# npoints_rbv = ddc.nsamples_rbv.value
# self.assertTrue(np.abs(npoints_rbv - 50) < 1,
# f"Expected to record 10 points but got {npoints_rbv}")
# print("AX:PSO: Checking basic 'toggled' event generation with DDC (3/3)")
# print("Test removed as there's some heavy jitter on the PSO output flag")
# # Move to acceleration position
# acc_dist = 0.5 * mot.velocity.value * mot.acceleration.value
# print(f"Acceleration distance: {acc_dist}")
# mot.move(mot.position - acc_dist)
# # Configure PSO in window mode
# cfg = {'bounds': (acc_dist, 30 + acc_dist + 1), 'pso_wavemode': "output"}
# dev.configure(cfg)
# dev.arm()
# # Configure the data capture
# cfg = {'ntotal': 10, 'trigger': DriveDataCaptureTrigger.PsoOutput}
# ddc.configure(cfg)
# ddc.arm()
# npoints_rbv = ddc.nsamples_rbv.value
# self.assertEqual(npoints_rbv, 0,
# f"DDC.stage should reset the number of DDC points but got: {npoints_rbv}")
# # Snake scan
# for ii in range(10):
# if ii % 2 == 0:
# mot.move(mot.position + 30 + 2 * acc_dist + 0.1)
# else:
# mot.move(mot.position - 30 - 2 * acc_dist - 0.1)
# time.sleep(0.5)
# npoints_rbv = ddc.nsamples_rbv.value
# self.assertTrue(np.abs(npoints_rbv - 10) < 1,
# f"Expected to record 10 points but got {npoints_rbv}")
def test_12_StepScan(self):
"""Test a simple blusky style step scan (via BEC) """Test a simple blusky style step scan (via BEC)
This module tests manual PSO trigger functionality. The goal is to This module tests manual PSO trigger functionality. The goal is to
ensure the basic operation of the PSO module and the polled feedback. ensure the basic operation of the PSO module and the polled feedback.
""" """
# Throws if IOC is not available or PVs are incorrect # Throws if IOC is not available or PVs are incorrect
pso = aa1AxisPsoDistance(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":PSO:", name="pso1") pso = aa1AxisPsoDistance(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":PSO:", name="pso1")
ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":DDC:", name="ddc") ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":DDC:", name="ddc")
mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="x") mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_IOC_AXIS, name="x")
mot.wait_for_connection() mot.wait_for_connection()
pso.wait_for_connection() pso.wait_for_connection()
ddc.wait_for_connection() ddc.wait_for_connection()
@@ -769,7 +683,7 @@ class AerotechAutomation1Test(unittest.TestCase):
ddc.disarm() ddc.disarm()
mot.unstage() mot.unstage()
t_start = time.time() t_start = time.time()
print("\ntest_12_StepScan: Simple step scan (via Ophyd)") print("\ntest_10_StepScan: Simple step scan (via Ophyd)")
pos_start = 112.0 pos_start = 112.0
scan_range = 180 scan_range = 180
@@ -787,7 +701,7 @@ class AerotechAutomation1Test(unittest.TestCase):
f"Expected to move to scan start position {pos_start}, now at {mot.position}", f"Expected to move to scan start position {pos_start}, now at {mot.position}",
) )
# PSO setup (very large pulse distance) # PSO setup (very large pulse distance)
pso.configure(d={"pso_n_pulse": 2, "pso_distance": 99999}) pso.configure(d={"pso_n_pulse": 2, "pso_distance": 9999})
# DDC setup (trigger at each point plus some extra) # DDC setup (trigger at each point plus some extra)
cfg = {"num_points_total": 1000 * (scan_steps + 1), "ddc_trigger": DriveDataCaptureTrigger.PsoOutput} cfg = {"num_points_total": 1000 * (scan_steps + 1), "ddc_trigger": DriveDataCaptureTrigger.PsoOutput}
ddc.configure(cfg) ddc.configure(cfg)
@@ -800,7 +714,7 @@ class AerotechAutomation1Test(unittest.TestCase):
for ii in range(len(positions)): for ii in range(len(positions)):
mot.move(positions[ii]) mot.move(positions[ii])
self.assertTrue( self.assertTrue(
np.abs(mot.position - positions[ii]) < MOTOR_TOL, np.abs(mot.position - positions[ii]) < AXIS_TOL,
f"Expected to reach {positions[ii]}, now is at {mot.position} on step {ii}", f"Expected to reach {positions[ii]}, now is at {mot.position} on step {ii}",
) )
pso.trigger() pso.trigger()
@@ -824,7 +738,7 @@ class AerotechAutomation1Test(unittest.TestCase):
f"Expected to record {len(positions)} DDC points, got: {npoints_rbv}", f"Expected to record {len(positions)} DDC points, got: {npoints_rbv}",
) )
def test_13_TomcatSequenceScan(self): def test_11_TomcatSequenceScan(self):
"""Test the zig-zag sequence scan from Tomcat (via BEC) """Test the zig-zag sequence scan from Tomcat (via BEC)
This module tests basic PSO distance functionality, like events and This module tests basic PSO distance functionality, like events and
@@ -832,9 +746,9 @@ class AerotechAutomation1Test(unittest.TestCase):
the PSO module and the polled feedback. the PSO module and the polled feedback.
""" """
# Throws if IOC is not available or PVs are incorrect # Throws if IOC is not available or PVs are incorrect
pso = aa1AxisPsoDistance(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":PSO:", name="pso1") pso = aa1AxisPsoDistance(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":PSO:", name="pso1")
ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":DDC:", name="ddc") ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":DDC:", name="ddc")
mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="x") mot = AerotechMotor(AA1_IOC_NAME + ":" + AA1_IOC_AXIS, name="x")
mot.wait_for_connection() mot.wait_for_connection()
pso.wait_for_connection() pso.wait_for_connection()
ddc.wait_for_connection() ddc.wait_for_connection()
@@ -842,7 +756,7 @@ class AerotechAutomation1Test(unittest.TestCase):
ddc.disarm() ddc.disarm()
mot.unstage() mot.unstage()
t_start = time.time() t_start = time.time()
print("\ntest_13_TomcatSequenceScan: TOMCAT Sequeence scan (via Ophyd)") print("\ntest_11_TomcatSequenceScan: TOMCAT Sequeence scan (via Ophyd)")
scan_start = 42.0 scan_start = 42.0
scan_range = 180 scan_range = 180
@@ -850,14 +764,14 @@ class AerotechAutomation1Test(unittest.TestCase):
scan_type = "PosNeg" scan_type = "PosNeg"
# Dynamic properties # Dynamic properties
scan_vel = 90 scan_vel = AXIS_VELOCITY
scan_acc = 500 scan_acc = 500
dist_safe = 10.0 dist_safe = 10.0
###################################################################### ######################################################################
print("\tConfigure") print("\tConfigure")
# Scan range # Scan range
dist_acc = 0.5 * scan_vel * scan_vel / scan_acc + dist_safe dist_acc = 0.5 * AXIS_VELOCITY * AXIS_VELOCITY / scan_acc + dist_safe
if scan_type in ["PosNeg", "Pos"]: if scan_type in ["PosNeg", "Pos"]:
pos_start = scan_start - dist_acc pos_start = scan_start - dist_acc
pos_end = scan_start + scan_range + dist_acc pos_end = scan_start + scan_range + dist_acc
@@ -868,8 +782,8 @@ class AerotechAutomation1Test(unittest.TestCase):
raise RuntimeError(f"Unexpected ScanType: {scan_type}") raise RuntimeError(f"Unexpected ScanType: {scan_type}")
# Motor setup # Motor setup
mot.velocity.set(scan_vel).wait() mot.velocity.set(AXIS_VELOCITY).wait()
mot.acceleration.set(scan_vel / scan_acc).wait() mot.acceleration.set(AXIS_VELOCITY / scan_acc).wait()
mot.move(pos_start) mot.move(pos_start)
self.assertTrue( self.assertTrue(
np.abs(mot.position - pos_start) < 1.0, np.abs(mot.position - pos_start) < 1.0,
@@ -897,12 +811,12 @@ class AerotechAutomation1Test(unittest.TestCase):
if scan_type in ["Pos", "Neg"]: if scan_type in ["Pos", "Neg"]:
mot.move(pos_end) mot.move(pos_end)
self.assertTrue( self.assertTrue(
np.abs(mot.position - pos_end) < MOTOR_TOL, np.abs(mot.position - pos_end) < AXIS_TOL,
f"Expected to reach {pos_end}, now is at {mot.position} on iteration {ii}", f"Expected to reach {pos_end}, now is at {mot.position} on iteration {ii}",
) )
mot.move(pos_start) mot.move(pos_start)
self.assertTrue( self.assertTrue(
np.abs(mot.position - pos_start) < MOTOR_TOL, np.abs(mot.position - pos_start) < AXIS_TOL,
f"Expected to rewind to {pos_start}, now at {mot.position} on iteration {ii}", f"Expected to rewind to {pos_start}, now at {mot.position} on iteration {ii}",
) )
@@ -910,13 +824,13 @@ class AerotechAutomation1Test(unittest.TestCase):
if ii % 2 == 0: if ii % 2 == 0:
mot.move(pos_end, wait=True) mot.move(pos_end, wait=True)
self.assertTrue( self.assertTrue(
np.abs(mot.position - pos_end) < MOTOR_TOL, np.abs(mot.position - pos_end) < AXIS_TOL,
f"Expected to reach {pos_end}, now at {mot.position} on iteration {ii}", f"Expected to reach {pos_end}, now at {mot.position} on iteration {ii}",
) )
else: else:
mot.move(pos_start, wait=True) mot.move(pos_start, wait=True)
self.assertTrue( self.assertTrue(
np.abs(mot.position - pos_start) < MOTOR_TOL, np.abs(mot.position - pos_start) < AXIS_TOL,
f"Expected to reach {pos_start}, now at {mot.position} on iteration {ii}", f"Expected to reach {pos_start}, now at {mot.position} on iteration {ii}",
) )
@@ -946,7 +860,7 @@ class AerotechAutomation1Test(unittest.TestCase):
f"Expected to record {num_repeat} DDC points, but got: {npoints_rbv}", f"Expected to record {num_repeat} DDC points, but got: {npoints_rbv}",
) )
def test_14_JinjaTomcatSequenceScan(self): def test_12_JinjaTomcatSequenceScan(self):
""" Run a test sequence scan with template substitution""" """ Run a test sequence scan with template substitution"""
# Load the test file # Load the test file
path = os.path.dirname(os.path.realpath(__file__)) path = os.path.dirname(os.path.realpath(__file__))
@@ -955,17 +869,18 @@ class AerotechAutomation1Test(unittest.TestCase):
templatetext = f.read() templatetext = f.read()
scanconfig = { scanconfig = {
"rotaxis": "RY",
"startpos": 42, "startpos": 42,
"scanrange": 180, "scanrange": 180,
"nrepeat": 13, "nrepeat": 13,
"scandir": "NEGPOS", "scandir": "NEGPOS",
"npoints": 1300, "npoints": 1300,
"scanvel": 150, "scanvel": AXIS_VELOCITY,
"scanacc": 500, "scanacc": 500,
"accdist": 0.5*150*150/500, "accdist": 0.5*AXIS_VELOCITY*AXIS_VELOCITY/500,
"psotrigger": "PsoEvent", "psotrigger": "PsoEvent",
"psoBoundsPos": [0.5*150*150/500, 180], "psoBoundsPos": [0.5*AXIS_VELOCITY*AXIS_VELOCITY/500, 180],
"psoBoundsNeg": [0.5*150*150/500, 180] "psoBoundsNeg": [0.5*AXIS_VELOCITY*AXIS_VELOCITY/500, 180]
} }
tm = jinja2.Template(templatetext) tm = jinja2.Template(templatetext)
text = tm.render(scan=scanconfig) text = tm.render(scan=scanconfig)
@@ -974,10 +889,10 @@ class AerotechAutomation1Test(unittest.TestCase):
# Throws if IOC is not available or PVs are incorrect # Throws if IOC is not available or PVs are incorrect
tsk = aa1Tasks(AA1_IOC_NAME + ":TASK:", name="tsk") tsk = aa1Tasks(AA1_IOC_NAME + ":TASK:", name="tsk")
ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_AXIS_NAME + ":DDC:", name="ddc") ddc = aa1AxisDriveDataCollection(AA1_IOC_NAME + ":" + AA1_IOC_AXIS + ":DDC:", name="ddc")
tsk.wait_for_connection() tsk.wait_for_connection()
ddc.wait_for_connection() ddc.wait_for_connection()
print("\nTOMCAT Sequeence scan (via Jinjad AeroScript)") print("\ntest_12_JinjaTomcatSequenceScan: TOMCAT Sequeence scan (via Jinjad AeroScript)")
# Copy file to controller and run it # Copy file to controller and run it
t_start = time.time() t_start = time.time()
@@ -33,7 +33,7 @@ program
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Internal parameters - dont use // Internal parameters - dont use
var $axis as axis = ROTY var $axis as axis = {{ scan.rotaxis or 'ROTY' }}
var $ii as integer var $ii as integer
var $axisFaults as integer = 0 var $axisFaults as integer = 0
var $iDdcSafeSpace as integer = 4096 var $iDdcSafeSpace as integer = 4096