Merge branch 'main' into gac-x02da_20250616T172122
This commit is contained in:
@@ -35,21 +35,22 @@ class aa1Controller(Device):
|
||||
"""Ophyd proxy class for the Aerotech Automation 1's core controller functionality"""
|
||||
|
||||
# ToDo: Add error subscription
|
||||
controllername = Component(EpicsSignalRO, "NAME", kind=Kind.config)
|
||||
serialnumber = Component(EpicsSignalRO, "SN", kind=Kind.config)
|
||||
apiversion = Component(EpicsSignalRO, "API_VERSION", kind=Kind.config)
|
||||
controllername = Component(EpicsSignalRO, "NAME", string=True, kind=Kind.config)
|
||||
serialnumber = Component(EpicsSignalRO, "SN", string=True, kind=Kind.config)
|
||||
apiversion = Component(EpicsSignalRO, "API_VERSION", string=True, kind=Kind.config)
|
||||
axiscount = Component(EpicsSignalRO, "AXISCOUNT", kind=Kind.config)
|
||||
taskcount = Component(EpicsSignalRO, "TASKCOUNT", kind=Kind.config)
|
||||
fastpoll = Component(EpicsSignalRO, "POLLTIME", auto_monitor=True, kind=Kind.normal)
|
||||
slowpoll = Component(EpicsSignalRO, "DRVPOLLTIME", auto_monitor=True, kind=Kind.normal)
|
||||
errno = Component(EpicsSignalRO, "ERRNO", auto_monitor=True, kind=Kind.normal)
|
||||
errnmsg = Component(EpicsSignalRO, "ERRMSG", auto_monitor=True, kind=Kind.normal)
|
||||
errnmsg = Component(EpicsSignalRO, "ERRMSG", string=True, auto_monitor=True, kind=Kind.normal)
|
||||
_set_ismc = Component(EpicsSignal, "SET", put_complete=True, kind=Kind.omitted)
|
||||
|
||||
USER_ACCESS = ["reset"]
|
||||
|
||||
def reset(self):
|
||||
""" Resets the Automation1 iSMC reloading the default configuration"""
|
||||
"""Resets the Automation1 iSMC reloading the default configuration. Note that this will
|
||||
erase all settings that were set during startup and not saved to the MCD file."""
|
||||
self._set_ismc.set(3).wait()
|
||||
|
||||
|
||||
|
||||
@@ -152,9 +152,8 @@ class aa1AxisDriveDataCollection(PSIDeviceBase, Device):
|
||||
self.configure(d=d)
|
||||
|
||||
# Stage the data collection if not in internally launced mode
|
||||
# NOTE: Scripted scans start acquiring from the scrits
|
||||
if "scan_type" in scan_args and scan_args["scan_type"] in ("scripted", "script"):
|
||||
self.arm()
|
||||
# NOTE: Scripted scans start acquiring from the scrits at kickoff
|
||||
self.arm()
|
||||
# Reset readback
|
||||
self.reset()
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ class AerotechPsoBase(PSIDeviceBase, Device):
|
||||
output = Component(EpicsSignalRO, "OUTPUT-RBV", auto_monitor=True, kind=Kind.normal)
|
||||
address = Component(EpicsSignalRO, "ARRAY-ADDR", kind=Kind.config)
|
||||
_eventSingle = Component(EpicsSignal, "EVENT:SINGLE", put_complete=True, kind=Kind.omitted)
|
||||
switch = Component(EpicsSignal, "SWITCH", put_complete=True, kind=Kind.omitted)
|
||||
|
||||
# ########################################################################
|
||||
# PSO Distance event module
|
||||
@@ -119,7 +120,6 @@ class AerotechPsoBase(PSIDeviceBase, Device):
|
||||
def fire(self, settle_time=0.1) -> None | DeviceStatus:
|
||||
"""Fire a single PSO event (i.e. manual software trigger)"""
|
||||
# Only trigger if distance was set to invalid
|
||||
logger.warning(f"[{self.name}] Triggerin...")
|
||||
self._eventSingle.set(1, settle_time=settle_time).wait()
|
||||
status = DeviceStatus(self)
|
||||
status.set_finished()
|
||||
@@ -139,6 +139,8 @@ class AerotechPsoBase(PSIDeviceBase, Device):
|
||||
w_pulse = d.get("pso_w_pulse", 200)
|
||||
n_pulse = d.get("pso_n_pulse", 1)
|
||||
|
||||
self.switch.set("Manual").wait()
|
||||
|
||||
# Configure the pulsed/toggled waveform
|
||||
if wmode in ["toggle", "toggled"]:
|
||||
# Switching to simple toggle mode
|
||||
@@ -161,12 +163,6 @@ class AerotechPsoBase(PSIDeviceBase, Device):
|
||||
else:
|
||||
raise RuntimeError(f"Unsupported window mode: {wmode}")
|
||||
|
||||
# Set PSO output data source
|
||||
# FIXME : This is essentially staging...
|
||||
if wmode in ["toggle", "toggled", "pulse", "pulsed"]:
|
||||
self.outSource.set("Waveform").wait()
|
||||
elif wmode in ["output", "flag"]:
|
||||
self.outSource.set("Window").wait()
|
||||
|
||||
|
||||
class aa1AxisPsoDistance(AerotechPsoBase):
|
||||
@@ -227,10 +223,6 @@ class aa1AxisPsoDistance(AerotechPsoBase):
|
||||
raise RuntimeError(f"Unsupported distace triggering mode: {pso_wavemode}")
|
||||
|
||||
old = self.read_configuration()
|
||||
# Disable everything
|
||||
self.winEvents.set("Off").wait()
|
||||
self.dstCounterEna.set("Off").wait()
|
||||
self.dstEventsEna.set("Off").wait()
|
||||
|
||||
# Configure distance generator (also resets counter to 0)
|
||||
self._distance_value = pso_distance
|
||||
@@ -288,33 +280,25 @@ class aa1AxisPsoDistance(AerotechPsoBase):
|
||||
"""Fire a single PSO event (i.e. manual software trigger)"""
|
||||
# Only trigger if distance was set to invalid
|
||||
# if self.dstDistanceVal.get() == 0:
|
||||
logger.warning(f"[{self.name}] Triggerin...")
|
||||
# logger.warning(f"[{self.name}] Triggerin...")
|
||||
return self.fire(settle_time)
|
||||
|
||||
def arm(self) -> None:
|
||||
"""Bluesky style stage"""
|
||||
"""Bluesky style stage
|
||||
|
||||
It re-arms the distance array and re-sets the distance counter at current position
|
||||
"""
|
||||
# Stage the PSO distance module and zero counter
|
||||
if isinstance(self._distance_value, (np.ndarray, list, tuple)):
|
||||
self.dstArrayRearm.set(1).wait()
|
||||
# Wait for polling
|
||||
sleep(0.5)
|
||||
# Start monitoring the counters if distance is valid
|
||||
if self.dstDistanceVal.get() > 0:
|
||||
self.dstEventsEna.set("On").wait()
|
||||
self.dstCounterEna.set("On").wait()
|
||||
# Set distance and wait for polling
|
||||
self.switch.set("Distance", settle_time=0.2).wait()
|
||||
|
||||
def disarm(self):
|
||||
"""Standard bluesky unstage"""
|
||||
# Ensure output is set to low
|
||||
# if self.output.value:
|
||||
# self.toggle()
|
||||
# Turn off window mode
|
||||
self.winOutput.set("Off").wait()
|
||||
self.winEvents.set("Off").wait()
|
||||
# Turn off distance mode
|
||||
self.dstEventsEna.set("Off").wait()
|
||||
self.dstCounterEna.set("Off").wait()
|
||||
# Disable output
|
||||
self.outSource.set("None").wait()
|
||||
# Sleep for one poll period
|
||||
sleep(0.2)
|
||||
self.switch.set("Manual", settle_time=0.2).wait()
|
||||
|
||||
def launch(self):
|
||||
"""Re-set the counters"""
|
||||
if isinstance(self._distance_value, (np.ndarray, list, tuple)):
|
||||
self.dstArrayRearm.set(1).wait()
|
||||
@@ -33,7 +33,7 @@ program
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Internal parameters - dont use
|
||||
var $axis as axis = ROTY
|
||||
var $axis as axis = {{ scan.rotaxis or 'ROTY' }}
|
||||
var $ii as integer
|
||||
var $axisFaults as integer = 0
|
||||
var $iDdcSafeSpace as integer = 4096
|
||||
|
||||
@@ -20,7 +20,7 @@ program
|
||||
|
||||
|
||||
// Internal
|
||||
var $axis as axis = ROTY
|
||||
var $axis as axis = {{ scan.rotaxis or 'ROTY' }}
|
||||
var $ii as integer
|
||||
var $axisFaults as integer = 0
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ interface.
|
||||
|
||||
@author: mohacsi_i
|
||||
"""
|
||||
import time
|
||||
from ophyd import Device, Component, EpicsSignal, EpicsSignalRO, Kind
|
||||
from ophyd.status import SubscriptionStatus
|
||||
|
||||
@@ -106,12 +107,8 @@ class aa1Tasks(PSIDeviceBase, Device):
|
||||
self.fileName.set(d["script_file"]).wait()
|
||||
if "script_text" in d:
|
||||
# Compile text for syntax checking
|
||||
# NOTE: This will load to 'script_file'
|
||||
# NOTE: This will overwrite 'script_file'
|
||||
self._fileWrite.set(d["script_text"], settle_time=0.2).wait()
|
||||
self.switch.set("Load").wait()
|
||||
# Check the result of load
|
||||
if self._failure.value:
|
||||
raise RuntimeError("Failed to load script, perhaps a syntax error?")
|
||||
|
||||
new = self.read_configuration()
|
||||
return (old, new)
|
||||
@@ -169,8 +166,22 @@ class aa1Tasks(PSIDeviceBase, Device):
|
||||
"""Bluesky style stage, prepare, but does not execute"""
|
||||
if self.taskIndex.get() in (0, 1, 2):
|
||||
logger.error(f"[{self.name}] Loading AeroScript on system task. Daring today are we?")
|
||||
if len(self.fileName.get())==0:
|
||||
self.fileName.set("bec.ascript", settle_time=0.1).wait()
|
||||
|
||||
self._failure.read()
|
||||
ts_old = float(self._failure.timestamp)
|
||||
def wait_until_new(*_, old_value, value, timestamp, **__):
|
||||
nonlocal ts_old
|
||||
result = bool(ts_old != timestamp) and (value!=-1)
|
||||
# print(f"{old_value}\t{value}\t{ts_old}\t{timestamp}\t{result}")
|
||||
return result
|
||||
|
||||
# Subscribe and wait for update
|
||||
status = SubscriptionStatus(self._failure, wait_until_new, settle_time=0.1)
|
||||
|
||||
# Load and check success
|
||||
status = self.switch.set("Load", settle_time=0.2)
|
||||
self.switch.set("Load", settle_time=0.0).wait()
|
||||
status.wait()
|
||||
if self._failure.value:
|
||||
raise RuntimeError("Failed to load task, please check the Aerotech IOC")
|
||||
@@ -191,11 +202,14 @@ class aa1Tasks(PSIDeviceBase, Device):
|
||||
|
||||
def complete(self) -> SubscriptionStatus:
|
||||
"""Wait for a RUNNING task"""
|
||||
# Sleep for foll period
|
||||
time.sleep(1)
|
||||
|
||||
timestamp_ = 0
|
||||
task_idx = int(self.taskIndex.get())
|
||||
|
||||
def not_running(*, value, timestamp, **_):
|
||||
nonlocal timestamp_
|
||||
# print(f"State {value[task_idx]} in {value}")
|
||||
result = value[task_idx] not in ["Running", 4]
|
||||
timestamp_ = timestamp
|
||||
return result
|
||||
|
||||
@@ -7,3 +7,7 @@ from .AerotechAutomation1 import (
|
||||
aa1GlobalVariableBindings,
|
||||
aa1AxisIo,
|
||||
)
|
||||
from .AerotechAutomation1Enums import (
|
||||
DriveDataCaptureInput,
|
||||
DriveDataCaptureTrigger,
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 $axis as axis = {{ scan.rotaxis or '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)
|
||||
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
|
||||
|
||||
|
||||
158
tomcat_bec/tcatSequenceScanRBV.ascript
Normal file
158
tomcat_bec/tcatSequenceScanRBV.ascript
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user