feat(devices): added device classes from ophyd_devices

This commit is contained in:
2024-05-08 13:10:40 +02:00
parent 6c7a63eb56
commit 1f66d629d0
11 changed files with 3563 additions and 1 deletions

View File

@@ -12,7 +12,7 @@ classifiers = [
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering",
]
dependencies = []
dependencies = ["ophyd_devices", "bec_lib"]
[project.optional-dependencies]
dev = ["black", "isort", "coverage", "pylint", "pytest", "pytest-random-order", "ophyd_devices", "bec_server"]

View File

@@ -0,0 +1,226 @@
# pylint: skip-file
from unittest import mock
import pytest
from tomcat_bec.devices.grashopper_tomcat import (
COLORMODE,
AutoMode,
DetectorState,
GrashopperError,
GrashopperTimeoutError,
GrashopperTOMCATSetup,
ImageBinning,
ImageMode,
MemoryPolling,
PixelFormat,
TriggerSource,
VideoMode,
)
def patch_dual_pvs(device):
for walk in device.walk_signals():
if not hasattr(walk.item, "_read_pv"):
continue
if not hasattr(walk.item, "_write_pv"):
continue
if walk.item._read_pv.pvname.endswith("_RBV"):
walk.item._read_pv = walk.item._write_pv
@pytest.fixture(scope="function")
def mock_GrashopperSetup():
mock_Grashopper = mock.MagicMock()
yield GrashopperTOMCATSetup(parent=mock_Grashopper)
# Fixture for scaninfo
@pytest.fixture(
params=[
{
"scan_id": "1234",
"scan_type": "step",
"num_points": 500,
"frames_per_trigger": 1,
"exp_time": 0.1,
"readout_time": 0.1,
},
{
"scan_id": "1234",
"scan_type": "step",
"num_points": 500,
"frames_per_trigger": 5,
"exp_time": 0.01,
"readout_time": 0,
},
{
"scan_id": "1234",
"scan_type": "fly",
"num_points": 500,
"frames_per_trigger": 1,
"exp_time": 1,
"readout_time": 0.2,
},
{
"scan_id": "1234",
"scan_type": "fly",
"num_points": 500,
"frames_per_trigger": 5,
"exp_time": 0.1,
"readout_time": 0.4,
},
]
)
def scaninfo(request):
return request.param
def test_initialize_detector(mock_GrashopperSetup):
"""Test the initialize_detector method."""
# Call the function you want to test
mock_GrashopperSetup.initialize_detector()
# Assert the correct methods are called with the expected arguments
mock_GrashopperSetup.parent.cam.acquire.put.assert_called_once_with(0)
mock_GrashopperSetup.parent.cam.acquire_time_auto.put.assert_called_once_with(
AutoMode.CONTINUOUS
)
mock_GrashopperSetup.parent.cam.gain_auto.put.assert_called_once_with(AutoMode.CONTINUOUS)
mock_GrashopperSetup.parent.cam.image_mode.put.assert_called_once_with(ImageMode.MULTIPLE)
mock_GrashopperSetup.parent.cam.image_binning.put.assert_called_once_with(ImageBinning.X1)
mock_GrashopperSetup.parent.cam.video_mode.put.assert_called_once_with(VideoMode.MODE0)
mock_GrashopperSetup.parent.cam.pixel_format.put.assert_called_once_with(PixelFormat.MONO16)
mock_GrashopperSetup.parent.cam.trigger_source.put.assert_called_once_with(
TriggerSource.SOFTWARE
)
mock_GrashopperSetup.parent.cam.memory_polling.put.assert_called_once_with(
MemoryPolling.SECONDS1
)
mock_GrashopperSetup.parent.cam.set_image_counter.put.assert_called_once_with(0)
def test_initialize_detector_backend(mock_GrashopperSetup):
"""Test the initialize_detector_backend method."""
# Call the function you want to test
mock_GrashopperSetup.initialize_detector_backend()
# Assert the correct methods are called with the expected arguments
mock_GrashopperSetup.parent.image.queue_size.put.assert_called_once_with(5)
mock_GrashopperSetup.parent.image.array_port.put.assert_called_once_with(
mock_GrashopperSetup.parent.cam.port_name.get()
)
mock_GrashopperSetup.parent.image.enable_cb.put.assert_called_once_with(1)
mock_GrashopperSetup.parent.image.set_array_counter.put.assert_called_once_with(0)
@pytest.mark.parametrize("exposure_time", [1, 0.1, 0.01, 0.001, 0.0001])
def test_set_exposure_time(mock_GrashopperSetup, exposure_time):
"""Test the set_exposure_time method."""
# Call the function you want to test
frame_rate = 1 / exposure_time
if frame_rate > mock_GrashopperSetup.low_frame_rate:
with pytest.raises(GrashopperError):
mock_GrashopperSetup.set_exposure_time(exposure_time)
else:
mock_GrashopperSetup.set_exposure_time(exposure_time)
mock_GrashopperSetup.parent.cam.frame_rate.put.assert_called_once_with(frame_rate)
def test_prepare_detector(mock_GrashopperSetup, scaninfo):
"""Test the prepare_detector method."""
# setup scaninof
for k, v in scaninfo.items():
setattr(mock_GrashopperSetup.parent.scaninfo, k, v)
# Call the function you want to test
with (
mock.patch.object(
mock_GrashopperSetup, "set_acquisition_params"
) as mock_set_acquisition_params,
mock.patch.object(mock_GrashopperSetup, "set_exposure_time") as mock_set_exposure_time,
):
mock_GrashopperSetup.prepare_detector()
# Assert the correct methods are called with the expected arguments
mock_GrashopperSetup.parent.cam.image_mode.put.assert_called_once_with(ImageMode.MULTIPLE)
mock_GrashopperSetup.parent.cam.acquire_time_auto.put.assert_called_once_with(
AutoMode.CONTINUOUS
)
mock_GrashopperSetup.parent.set_trigger.assert_called_once_with(TriggerSource.SOFTWARE)
mock_GrashopperSetup.parent.cam.set_image_counter.put.assert_called_once_with(0)
mock_set_acquisition_params.assert_called_once()
mock_set_exposure_time.assert_called_once_with(scaninfo["exp_time"])
def test_prepare_detector_backend(mock_GrashopperSetup):
"""Test the prepare_detector_backend method."""
# Call the function you want to test
mock_GrashopperSetup.prepare_detector_backend()
mock_GrashopperSetup.parent.image.set_array_counter.put.assert_called_once_with(0)
assert mock_GrashopperSetup.monitor_thread is None
assert not mock_GrashopperSetup.stop_monitor
@pytest.mark.parametrize("detector_state", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
def test_arm_acquisition(mock_GrashopperSetup, detector_state):
"""Test the arm_acquisition method."""
# Call the function you want to test
mock_GrashopperSetup.parent.cam.detector_state.get.return_value = detector_state
mock_GrashopperSetup.parent.timeout = 0.1
if detector_state != DetectorState.WAITING:
with pytest.raises(GrashopperTimeoutError):
mock_GrashopperSetup.arm_acquisition()
assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1
assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(1)
else:
mock_GrashopperSetup.arm_acquisition()
assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1
assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(1)
@pytest.mark.parametrize("trigger_source", [0, 1, 2, 3])
def test_on_trigger(mock_GrashopperSetup, trigger_source):
"""Test the on_trigger method."""
# Call the function you want to test
mock_GrashopperSetup.parent.cam.trigger_source.get.return_value = trigger_source
with mock.patch.object(mock_GrashopperSetup, "send_data") as mock_send_data:
mock_GrashopperSetup.on_trigger()
# Assert the correct methods are called with the expected arguments
if trigger_source == TriggerSource.SOFTWARE:
mock_GrashopperSetup.parent.cam.software_trigger_device.put.assert_called_once_with(1)
assert mock_send_data.call_count == 1
def test_set_acquisition_params(mock_GrashopperSetup, scaninfo):
"""Test the set_acquisition_params method."""
# Setup scaninfo
mock_GrashopperSetup.parent.scaninfo.num_points = scaninfo["num_points"]
mock_GrashopperSetup.parent.scaninfo.frames_per_trigger = scaninfo["frames_per_trigger"]
# Call the function you want to test
mock_GrashopperSetup.set_acquisition_params()
# Assert the correct methods are called with the expected arguments
expected_num_images = scaninfo["num_points"] * scaninfo["frames_per_trigger"]
mock_GrashopperSetup.parent.cam.num_images.put.assert_called_once_with(expected_num_images)
@pytest.mark.parametrize("detector_state", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
def test_stop_detector(mock_GrashopperSetup, detector_state):
"""Test the arm_acquisition method."""
# Call the function you want to test
mock_GrashopperSetup.parent.cam.detector_state.get.return_value = detector_state
mock_GrashopperSetup.parent.timeout = 0.1
if detector_state != DetectorState.IDLE:
with pytest.raises(GrashopperTimeoutError):
mock_GrashopperSetup.stop_detector()
assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 2
assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(0)
else:
mock_GrashopperSetup.stop_detector()
assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1
assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(0)

View File

@@ -0,0 +1,94 @@
eyex:
readoutPriority: baseline
description: X-ray eye translation x
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: MTEST-X05LA-ES2-XRAYEYE:M1
deviceTags:
- xray-eye
onFailure: buffer
enabled: true
readOnly: false
softwareTrigger: false
eyey:
readoutPriority: baseline
description: X-ray eye translation y
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: MTEST-X05LA-ES2-XRAYEYE:M2
deviceTags:
- xray-eye
onFailure: buffer
enabled: true
readOnly: false
softwareTrigger: false
eyez:
readoutPriority: baseline
description: X-ray eye translation z
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: MTEST-X05LA-ES2-XRAYEYE:M3
deviceTags:
- xray-eye
onFailure: buffer
enabled: true
readOnly: false
softwareTrigger: false
femto_mean_curr:
readoutPriority: monitored
description: Femto mean current
deviceClass: ophyd.EpicsSignal
deviceConfig:
auto_monitor: true
read_pv: MTEST-X05LA-ES2-XRAYEYE:FEMTO-MEAN-CURR
deviceTags:
- xray-eye
onFailure: buffer
enabled: true
readOnly: true
softwareTrigger: false
es1_roty:
description: 'Test rotation stage'
deviceClass: ophyd_devices.epics.devices.EpicsMotorX
deviceConfig: {prefix: 'X02DA-ES1-SMP1:ROTY'}
onFailure: buffer
enabled: true
readoutPriority: monitored
# es1_tasks:
# description: 'AA1 task management interface'
# deviceClass: tomcat_bec.devices.aa1Tasks
# deviceConfig: {prefix: 'X02DA-ES1-SMP1:TASK:'}
# onFailure: buffer
# enabled: true
# readoutPriority: monitored
# es1_psod:
# description: 'AA1 PSO output interface'
# deviceClass: tomcat_bec.devices.aa1AxisPsoDistance
# deviceConfig: {prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'}
# onFailure: buffer
# enabled: true
# readoutPriority: monitored
# es1_ddaq:
# description: 'AA1 drive data collection interface'
# deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
# deviceConfig: {prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'}
# onFailure: buffer
# enabled: true
# readoutPriority: monitored
camera:
description: Grashopper Camera
deviceClass: tomcat_bec.devices.GrashopperTOMCAT
deviceConfig:
prefix: 'X02DA-PG-USB:'
deviceTags:
- camera
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: true

View File

@@ -0,0 +1,10 @@
from .aerotech.AerotechAutomation1 import (
EpicsMotorX,
aa1AxisDriveDataCollection,
aa1AxisPsoDistance,
aa1Controller,
aa1GlobalVariableBindings,
aa1GlobalVariables,
aa1Tasks,
)
from .grashopper_tomcat import GrashopperTOMCAT

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,901 @@
from enum import Enum
class TomcatSequencerState:
IDLE = 0
READY = 1
ARMED = 2
RUNNING = 3
CHANGING = 4
ERROR = 15
class AxisDataSignal:
PositionFeedback = 0
PositionCommand = 1
PositionError = 2
VelocityFeedback = 3
VelocityCommand = 4
VelocityError = 5
AccelerationCommand = 6
CurrentError = 9
PositionCommandRaw = 12
VelocityCommandRaw = 13
AuxiliaryFeedback = 14
DigitalInput = 15
DigitalOutput = 16
FixtureOffset = 18
CoordinatedPositionTarget = 41
DriveStatus = 42
AxisStatus = 43
AxisFault = 44
AccelerationCommandRaw = 45
PositionCalibrationAll = 50
PositionFeedbackRollover = 63
PositionCommandRollover = 64
VelocityFeedbackAverage = 65
CurrentFeedbackAverage = 66
AxisParameter = 68
Backlash = 72
HomeState = 73
PositionCalibration2D = 74
NormalcyDebug = 75
TotalMoveTime = 76
JerkCommandRaw = 78
ProgramPositionCommand = 79
PositionOffset = 80
PositionCommandRawBackwardsDiff = 82
VelocityCommandRawBackwardsDiffDelta = 83
DriveStatusActual = 85
ProgramPositionFeedback = 89
JogTrajectoryStatus = 94
PingTest = 95
AccelerationTime = 109
DecelerationTime = 110
AccelerationRate = 111
DecelerationRate = 112
AccelerationType = 113
DecelerationType = 114
AccelerationMode = 115
DecelerationMode = 116
ProgramPosition = 124
SpeedTarget = 128
PositionCommandPacket = 131
DriveSmcMotionState = 132
PositionCommandRawCal = 140
VelocityCommandRawCal = 141
VelocityCommandDrive = 142
AccelerationCommandDrive = 143
GalvoLaserOutputRaw = 144
DriveInterfacePacketInt32 = 147
DriveInterfacePacketInt16 = 148
DriveInterfacePacketInt8 = 149
DriveInterfacePacketDouble = 150
DriveInterfacePacketFloat = 151
DriveInterfaceCommandCode = 152
AccelerationFeedback = 153
AccelerationCommandRawCal = 154
PositionCalibrationAllDrive = 155
BacklashTarget = 156
DriveMotionRate = 158
DriveMotionDelay = 159
CalibrationAdjustmentValue = 160
ServoRounding = 161
FeedforwardCurrent = 162
DriveInterfacePacketInfoBitValue = 164
AccelerationError = 165
SuppressedFaults = 167
DriveInterfacePacketStreamingData = 168
PositionCommandRawUnfiltered = 169
TransitionOffsetErrors = 170
FreezeVelocityCommand = 179
FreezeVelocityFeedback = 180
InternalPositionOffsets = 181
StatusHighLevelOffsetsLastMsec = 182
ProgramVelocityCommand = 183
ProgramVelocityFeedback = 184
DriveMotionDelayLive = 185
DriveCommunicationDelay = 186
DriveCommunicationDelayLive = 187
DriveInterfacePacketResponseInt32 = 189
DriveInterfacePacketResponseInt16 = 190
DriveInterfacePacketResponseInt8 = 191
DriveInterfacePacketResponseDouble = 192
DriveInterfacePacketResponseFloat = 193
DriveInterfacePacketBit = 194
DriveInterfacePacketResponseBit = 195
SpeedTargetActual = 196
CoordinatedDistanceRemaining = 199
SafeZoneState = 230
PositionErrorGalvo = 235
MoveReferencePosition = 237
MoveReferenceCutterOffset = 250
MoveReferenceCornerOffset = 251
MoveReferenceTotalOffset = 252
DistanceLog = 264
AutoFocusError = 295
GalvoLaserOutputRawAdvance = 296
GalvoLaserOnDelay = 297
GalvoLaserOffDelay = 298
CalibrationAdjustmentState = 301
AccuracyCorrectionStartingPosition = 302
AccuracyCorrectionEndingPosition = 303
DriveCommandsDelayed = 309
DriveCommandsLost = 310
StoStatus = 327
DriveAssert = 336
PrimaryFeedback = 366
AccelerationSCurvePercentage = 371
DecelerationSCurvePercentage = 372
GantryMarkerDifference = 390
PrimaryFeedbackStatus = 392
HomeTargetPosition = 398
GantryRealignmentMoveTargetPosition = 399
GantryDriveControlRealignmentState = 400
DriveInterfacePositionCommandPhysical = 434
DriveControlReason = 435
CurrentFeedback = 7
CurrentCommand = 8
AnalogInput0 = 10
AnalogInput1 = 11
PhaseACurrentFeedback = 19
PhaseBCurrentFeedback = 20
EncoderSine = 21
EncoderCosine = 22
AnalogInput2 = 23
AnalogInput3 = 24
FrequencyResponseBefore = 25
FrequencyResponseAfter = 26
AnalogOutput0 = 31
AnalogOutput1 = 32
AnalogOutput2 = 33
AnalogOutput3 = 34
DriveMemoryInt32 = 35
DriveMemoryFloat = 36
DriveMemoryDouble = 37
PsoStatus = 38
DriveTimerDebug = 39
PositionFeedbackDrive = 77
PositionCommandDrive = 84
DriveMemoryInt16 = 125
DriveMemoryInt8 = 126
PsoCounter0 = 171
PsoCounter1 = 172
PsoCounter2 = 173
PsoWindow0 = 174
PsoWindow1 = 175
DriveDataCaptureSamples = 176
PositionCommandGalvo = 178
PrimaryEnDatAbsolutePosition = 197
ControlEffort = 201
PhaseAVoltageCommand = 208
PhaseBVoltageCommand = 209
PhaseCVoltageCommand = 210
AmplifierPeakCurrent = 211
FpgaVersion = 212
DriveTypeId = 213
PsoWindow0ArrayIndex = 214
PsoWindow1ArrayIndex = 215
PsoDistanceArrayIndex = 216
AmplifierTemperature = 217
PsoBitArrayIndex = 218
MxAbsolutePosition = 219
ServoUpdateRate = 220
SettlingTime = 221
InternalStatusCode = 222
FirmwareVersionMajor = 223
FirmwareVersionMinor = 224
FirmwareVersionPatch = 225
FirmwareVersionBuild = 226
DriveTimerDebugMax = 227
MarkerSearchDistance = 228
PositionFeedbackGalvo = 234
LatchedMarkerPosition = 236
PrimaryBissAbsolutePosition = 255
FaultPositionFeedback = 258
MotorCommutationAngle = 259
ExpansionBoardOption = 260
BusVoltage = 261
PiezoVoltageCommand = 262
PiezoVoltageFeedback = 263
TimeSinceReset = 273
MaximumVoltage = 274
CommandOutputType = 275
DriveFeedforwardOutput = 290
LastTickCounter = 291
BoardRevision = 292
GalvoLaserOutput = 294
GalvoLaserPowerCorrectionOutput = 299
CapacitanceSensorRawPosition = 300
PositionCalibrationGalvo = 304
BusVoltageNegative = 325
ProcessorTemperature = 326
InternalStatusTimestamp = 328
AnalogSensorInput = 329
MotorTemperature = 330
PrimaryBissStatus = 332
PsoExternalSyncFrequency = 337
EncoderSineRaw = 346
EncoderCosineRaw = 347
FpgaTemperature = 353
PrimaryEnDatStatus = 355
DriveTimerHighPriorityThread = 356
DriveTimerLowPriorityThread = 357
DriveTimerLowPriorityPacket = 358
DriveTimerServoPacket = 359
DriveTimerServoThread = 360
DriveTimerCurrentPacket = 361
DriveTimerCommonCoreThread = 362
DriveTimerServoCorePacket0 = 363
DriveTimerServoCorePacket1 = 364
MultiplierOption = 365
ServoLoopFeedbackInput0 = 367
ServoLoopFeedbackInput1 = 368
FaultSubcode = 376
ProcessorTemperatureMax = 378
DriveTimerHyperWireDma = 381
AmplifierTemperatureMax = 382
AuxiliaryEnDatAbsolutePosition = 383
AuxiliaryEnDatStatus = 384
AuxiliaryBissAbsolutePosition = 385
AuxiliaryBissStatus = 386
PsoOption = 387
DriveArraySize = 388
RatedMotorSupplyVoltageOption = 389
AbsoluteEncoderOption = 391
AuxiliaryFeedbackStatus = 393
AmplifierStatus = 394
LatchedCwLimitPosition = 395
LatchedCcwLimitPosition = 396
GalvoLaserFpgaTransitionDelay = 397
PiezoAccumulatedCharge = 401
PiezoChargingTime = 402
PrimarySsiAbsolutePosition = 403
PrimarySsiStatus = 404
AuxiliarySsiAbsolutePosition = 405
AuxiliarySsiStatus = 406
PsoDistanceActiveDistance = 407
PsoWindow0ActiveLowerBound = 408
PsoWindow0ActiveUpperBound = 409
PsoWindow1ActiveLowerBound = 410
PsoWindow1ActiveUpperBound = 411
PsoWaveformActiveTotalTime = 412
PsoWaveformActiveOnTime = 413
PsoWaveformActivePulseCount = 414
PsoEventActiveBitValue = 415
DriveTimerDriveBasedControllerOutputDma = 419
DriveTimerPcieInboundFsm = 420
PrimaryFeedbackServo = 425
AuxiliaryFeedbackServo = 426
DriveStackUsage = 427
ShuntResistorTemperature = 436
class TaskDataSignal:
ProgramLineNumber = 17
CoordinatedFlags = 40
CoordinatedArcStartAngle = 53
CoordinatedArcEndAngle = 54
CoordinatedArcRadius = 55
CoordinatedArcRadiusError = 56
CoordinatedPositionCommand = 57
CoordinatedSpeedCommand = 58
CoordinatedAccelerationCommand = 59
CoordinatedTotalDistance = 60
CoordinatedPercentDone = 61
CoordinatedPositionCommandBackwardsDiff = 62
TaskParameter = 69
TaskError = 70
TaskWarning = 71
CoordinatedSpeedTargetActual = 86
DependentCoordinatedSpeedTargetActual = 87
ActiveFixtureOffset = 88
TaskStatus0 = 90
TaskStatus1 = 91
TaskStatus2 = 92
SpindleSpeedTarget = 93
CoordinateSystem1I = 96
CoordinateSystem1J = 97
CoordinateSystem1K = 98
CoordinateSystem1Plane = 99
ToolNumberActive = 100
Mfo = 101
CoordinatedSpeedTarget = 102
DependentCoordinatedSpeedTarget = 103
CoordinatedAccelerationRate = 104
CoordinatedDecelerationRate = 105
CoordinatedAccelerationTime = 106
CoordinatedDecelerationTime = 107
TaskMode = 108
TaskState = 117
TaskStateInternal = 118
ExecutionMode = 121
EnableAlignmentAxes = 127
CoordinatedGalvoLaserOutput = 133
CoordinatedMotionRate = 145
CoordinatedTaskCommand = 146
EnableState = 166
LookaheadMovesExamined = 200
ProfileControlMask = 231
CoordinatedArcRadiusReciprocal = 253
MotionEngineStage = 254
CoordinatedTimeScale = 256
CoordinatedTimeScaleDerivative = 257
IfovSpeedScale = 266
IfovSpeedScaleAverage = 267
IfovGenerationFrameCounter = 268
IfovGenerationTimeOriginal = 269
IfovGenerationTimeModified = 270
IfovCoordinatedPositionCommand = 271
IfovCoordinatedSpeedCommand = 272
IfovCenterPointH = 276
IfovCenterPointV = 277
IfovTrajectoryCount = 278
IfovTrajectoryIndex = 279
IfovAttemptCode = 280
IfovGenerationFrameIndex = 281
IfovMaximumVelocity = 282
IfovIdealVelocity = 283
TaskInternalDebug = 284
IfovCoordinatedAccelerationCommand = 285
IfovFovPositionH = 286
IfovFovPositionV = 287
IfovFovDimensionH = 288
IfovFovDimensionV = 289
MotionBufferElements = 311
MotionBufferMoves = 312
MotionLineNumber = 313
MotionBufferRetraceMoves = 314
MotionBufferRetraceElements = 315
MotionBufferIndex = 316
MotionBufferIndexLookahead = 317
MotionBufferProcessingBlocked = 318
ActiveMoveValid = 319
TaskExecutionLines = 320
SchedulerTaskHolds = 321
SchedulerProgramLoopRuns = 322
SchedulerTaskBlocked = 323
CriticalSectionsActive = 324
AxesSlowdownReason = 331
TaskExecutionTime = 333
TaskExecutionTimeMaximum = 334
TaskExecutionLinesMaximum = 335
LookaheadDecelReason = 338
LookaheadDecelMoves = 339
LookaheadDecelDistance = 340
ProgramCounter = 341
StackPointer = 342
FramePointer = 343
StringStackPointer = 344
ProgramLineNumberSourceFileId = 349
MotionLineNumberSourceFileId = 350
ProgramLineNumberSourcePathId = 351
MotionLineNumberSourcePathId = 352
StringArgumentStackPointer = 354
CoordinatedAccelerationSCurvePercentage = 369
CoordinatedDecelerationSCurvePercentage = 370
DependentCoordinatedAccelerationRate = 373
DependentCoordinatedDecelerationRate = 374
CriticalSectionTimeout = 375
CommandQueueCapacity = 421
CommandQueueUnexecutedCount = 422
CommandQueueTimesEmptied = 423
CommandQueueExecutedCount = 424
class SystemDataSignal:
VirtualBinaryInput = 46
VirtualBinaryOutput = 47
VirtualRegisterInput = 48
VirtualRegisterOutput = 49
Timer = 51
TimerPerformance = 52
GlobalReal = 67
CommunicationRealTimeErrors = 81
LibraryCommand = 119
DataCollectionSampleTime = 120
DataCollectionSampleIndex = 129
ModbusClientConnected = 134
ModbusServerConnected = 135
ModbusClientError = 136
ModbusServerError = 137
StopWatchTimer = 157
ScopetrigId = 163
EstimatedProcessorUsage = 177
DataCollectionStatus = 188
SignalLogState = 198
SafeZoneViolationMask = 207
SafeZoneActiveMask = 229
ModbusClientInputWords = 240
ModbusClientOutputWords = 241
ModbusClientInputBits = 242
ModbusClientOutputBits = 243
ModbusClientOutputBitsStatus = 244
ModbusClientOutputWordsStatus = 245
ModbusServerInputWords = 246
ModbusServerOutputWords = 247
ModbusServerInputBits = 248
ModbusServerOutputBits = 249
SystemParameter = 265
ThermoCompSensorTemperature = 305
ThermoCompControllingTemperature = 306
ThermoCompCompensatingTemperature = 307
ThermoCompStatus = 308
GlobalInteger = 345
AliveAxesMask = 348
SignalLogPointsStored = 377
ControllerInitializationWarning = 379
StopWatchTimerMin = 416
StopWatchTimerMax = 417
StopWatchTimerAvg = 418
EthercatEnabled = 428
EthercatError = 429
EthercatTxPdo = 430
EthercatTxPdoSize = 431
EthercatRxPdo = 432
EthercatRxPdoSize = 433
EthercatState = 437
ModbusClientEnabled = 438
ModbusServerEnabled = 439
class DataCollectionFrequency:
Undefined = 0
Fixed1kHz = 1
Fixed10kHz = 2
Fixed20kHz = 3
Fixed100kHz = 4
Fixed200kHz = 5
class DataCollectionMode:
Snapshot = 0
Continouous = 1
# Specifies the PSO distance input settings for the XC4e drive.
class PsoDistanceInput:
XC4PrimaryFeedback = 130
XC4AuxiliaryFeedback = 131
XC4SyncPortA = 132
XC4SyncPortB = 133
XC4DrivePulseStream = 134
XC4ePrimaryFeedback = 135
XC4eAuxiliaryFeedback = 136
XC4eSyncPortA = 137
XC4eSyncPortB = 138
XC4eDrivePulseStream = 139
class PsoWindowInput:
XC4PrimaryFeedback = 130
XC4AuxiliaryFeedback = 131
XC4SyncPortA = 132
XC4SyncPortB = 133
XC4DrivePulseStream = 134
XC4ePrimaryFeedback = 135
XC4eAuxiliaryFeedback = 136
XC4eSyncPortA = 137
XC4eSyncPortB = 138
XC4eDrivePulseStream = 139
XL5ePrimaryFeedback = (145,)
XL5eAuxiliaryFeedback = (146,)
XL5eSyncPortA = (147,)
XL5eSyncPortB = (148,)
XL5eDrivePulseStream = (149,)
# @brief Specifies the PSO output pin settings for each drive.
class XC4ePsoOutputPin:
DedicatedOutput = 111
AuxiliaryMarkerDifferential = 112
AuxiliaryMarkerSingleEnded = 113
class XC4PsoOutputPin:
DedicatedOutput = 108
AuxiliaryMarkerDifferential = 109
AuxiliaryMarkerSingleEnded = 110
"""
# @brief Specifies the PSO distance input settings for each drive.
class Automation1PsoDistanceInput:
Automation1PsoDistanceInput_GL4PrimaryFeedbackAxis1 = 100,
Automation1PsoDistanceInput_GL4PrimaryFeedbackAxis2 = 101,
Automation1PsoDistanceInput_GL4IfovFeedbackAxis1 = 102,
Automation1PsoDistanceInput_GL4IfovFeedbackAxis2 = 103,
Automation1PsoDistanceInput_GL4AuxiliaryFeedbackAxis1 = 104,
Automation1PsoDistanceInput_GL4AuxiliaryFeedbackAxis2 = 105,
Automation1PsoDistanceInput_GL4SyncPortA = 106,
Automation1PsoDistanceInput_GL4SyncPortB = 107,
Automation1PsoDistanceInput_GL4DrivePulseStreamAxis1 = 108,
Automation1PsoDistanceInput_GL4DrivePulseStreamAxis2 = 109,
Automation1PsoDistanceInput_XL4sPrimaryFeedback = 110,
Automation1PsoDistanceInput_XL4sAuxiliaryFeedback = 111,
Automation1PsoDistanceInput_XL4sSyncPortA = 112,
Automation1PsoDistanceInput_XL4sSyncPortB = 113,
Automation1PsoDistanceInput_XL4sDrivePulseStream = 114,
Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis1 = 115,
Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis2 = 116,
Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis3 = 117,
Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis4 = 118,
Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis5 = 119,
Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis6 = 120,
Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis1 = 121,
Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis2 = 122,
Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis3 = 123,
Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis4 = 124,
Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis5 = 125,
Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis6 = 126,
Automation1PsoDistanceInput_XR3SyncPortA = 127,
Automation1PsoDistanceInput_XR3SyncPortB = 128,
Automation1PsoDistanceInput_XR3DrivePulseStream = 129,
Automation1PsoDistanceInput_XC4PrimaryFeedback = 130,
Automation1PsoDistanceInput_XC4AuxiliaryFeedback = 131,
Automation1PsoDistanceInput_XC4SyncPortA = 132,
Automation1PsoDistanceInput_XC4SyncPortB = 133,
Automation1PsoDistanceInput_XC4DrivePulseStream = 134,
XC4ePrimaryFeedback = 135,
XC4eAuxiliaryFeedback = 136,
XC4eSyncPortA = 137,
XC4eSyncPortB = 138,
XC4eDrivePulseStream = 139,
Automation1PsoDistanceInput_XC6ePrimaryFeedback = 140,
Automation1PsoDistanceInput_XC6eAuxiliaryFeedback = 141,
Automation1PsoDistanceInput_XC6eSyncPortA = 142,
Automation1PsoDistanceInput_XC6eSyncPortB = 143,
Automation1PsoDistanceInput_XC6eDrivePulseStream = 144,
Automation1PsoDistanceInput_XL5ePrimaryFeedback = 145,
Automation1PsoDistanceInput_XL5eAuxiliaryFeedback = 146,
Automation1PsoDistanceInput_XL5eSyncPortA = 147,
Automation1PsoDistanceInput_XL5eSyncPortB = 148,
Automation1PsoDistanceInput_XL5eDrivePulseStream = 149,
Automation1PsoDistanceInput_XC2PrimaryFeedback = 150,
Automation1PsoDistanceInput_XC2AuxiliaryFeedback = 151,
Automation1PsoDistanceInput_XC2DrivePulseStream = 152,
Automation1PsoDistanceInput_XC2ePrimaryFeedback = 153,
Automation1PsoDistanceInput_XC2eAuxiliaryFeedback = 154,
Automation1PsoDistanceInput_XC2eDrivePulseStream = 155,
Automation1PsoDistanceInput_XL2ePrimaryFeedback = 156,
Automation1PsoDistanceInput_XL2eAuxiliaryFeedback = 157,
Automation1PsoDistanceInput_XL2eSyncPortA = 158,
Automation1PsoDistanceInput_XL2eSyncPortB = 159,
Automation1PsoDistanceInput_XL2eDrivePulseStream = 160,
Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis1 = 161,
Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis2 = 162,
Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis3 = 163,
Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis4 = 164,
Automation1PsoDistanceInput_XI4AuxiliaryFeedback1 = 165,
Automation1PsoDistanceInput_XI4AuxiliaryFeedback2 = 166,
Automation1PsoDistanceInput_XI4AuxiliaryFeedback3 = 167,
Automation1PsoDistanceInput_XI4AuxiliaryFeedback4 = 168,
Automation1PsoDistanceInput_XI4SyncPortA = 169,
Automation1PsoDistanceInput_XI4SyncPortB = 170,
Automation1PsoDistanceInput_XI4DrivePulseStreamAxis1 = 171,
Automation1PsoDistanceInput_XI4DrivePulseStreamAxis2 = 172,
Automation1PsoDistanceInput_XI4DrivePulseStreamAxis3 = 173,
Automation1PsoDistanceInput_XI4DrivePulseStreamAxis4 = 174,
Automation1PsoDistanceInput_iXC4PrimaryFeedback = 175,
Automation1PsoDistanceInput_iXC4AuxiliaryFeedback = 176,
Automation1PsoDistanceInput_iXC4SyncPortA = 177,
Automation1PsoDistanceInput_iXC4SyncPortB = 178,
Automation1PsoDistanceInput_iXC4DrivePulseStream = 179,
Automation1PsoDistanceInput_iXC4ePrimaryFeedback = 180,
Automation1PsoDistanceInput_iXC4eAuxiliaryFeedback = 181,
Automation1PsoDistanceInput_iXC4eSyncPortA = 182,
Automation1PsoDistanceInput_iXC4eSyncPortB = 183,
Automation1PsoDistanceInput_iXC4eDrivePulseStream = 184,
Automation1PsoDistanceInput_iXC6ePrimaryFeedback = 185,
Automation1PsoDistanceInput_iXC6eAuxiliaryFeedback = 186,
Automation1PsoDistanceInput_iXC6eSyncPortA = 187,
Automation1PsoDistanceInput_iXC6eSyncPortB = 188,
Automation1PsoDistanceInput_iXC6eDrivePulseStream = 189,
Automation1PsoDistanceInput_iXL5ePrimaryFeedback = 190,
Automation1PsoDistanceInput_iXL5eAuxiliaryFeedback = 191,
Automation1PsoDistanceInput_iXL5eSyncPortA = 192,
Automation1PsoDistanceInput_iXL5eSyncPortB = 193,
Automation1PsoDistanceInput_iXL5eDrivePulseStream = 194,
Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis1 = 195,
Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis2 = 196,
Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis3 = 197,
Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis4 = 198,
Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis5 = 199,
Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis6 = 200,
Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis1 = 201,
Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis2 = 202,
Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis3 = 203,
Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis4 = 204,
Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis5 = 205,
Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis6 = 206,
Automation1PsoDistanceInput_iXR3SyncPortA = 207,
Automation1PsoDistanceInput_iXR3SyncPortB = 208,
Automation1PsoDistanceInput_iXR3DrivePulseStream = 209,
Automation1PsoDistanceInput_GI4DrivePulseStreamAxis1 = 210,
Automation1PsoDistanceInput_GI4DrivePulseStreamAxis2 = 211,
Automation1PsoDistanceInput_GI4DrivePulseStreamAxis3 = 212,
Automation1PsoDistanceInput_iXC2PrimaryFeedback = 213,
Automation1PsoDistanceInput_iXC2AuxiliaryFeedback = 214,
Automation1PsoDistanceInput_iXC2DrivePulseStream = 215,
Automation1PsoDistanceInput_iXC2ePrimaryFeedback = 216,
Automation1PsoDistanceInput_iXC2eAuxiliaryFeedback = 217,
Automation1PsoDistanceInput_iXC2eDrivePulseStream = 218,
Automation1PsoDistanceInput_iXL2ePrimaryFeedback = 219,
Automation1PsoDistanceInput_iXL2eAuxiliaryFeedback = 220,
Automation1PsoDistanceInput_iXL2eSyncPortA = 221,
Automation1PsoDistanceInput_iXL2eSyncPortB = 222,
Automation1PsoDistanceInput_iXL2eDrivePulseStream = 223,
Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis1 = 224,
Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis2 = 225,
Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis3 = 226,
Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis4 = 227,
Automation1PsoDistanceInput_iXI4AuxiliaryFeedback1 = 228,
Automation1PsoDistanceInput_iXI4AuxiliaryFeedback2 = 229,
Automation1PsoDistanceInput_iXI4AuxiliaryFeedback3 = 230,
Automation1PsoDistanceInput_iXI4AuxiliaryFeedback4 = 231,
Automation1PsoDistanceInput_iXI4SyncPortA = 232,
Automation1PsoDistanceInput_iXI4SyncPortB = 233,
Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis1 = 234,
Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis2 = 235,
Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis3 = 236,
Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis4 = 237,
# @brief Specifies the PSO window input settings for each drive.
class Automation1PsoWindowInput:
Automation1PsoWindowInput_GL4PrimaryFeedbackAxis1 = 100,
Automation1PsoWindowInput_GL4PrimaryFeedbackAxis2 = 101,
Automation1PsoWindowInput_GL4IfovFeedbackAxis1 = 102,
Automation1PsoWindowInput_GL4IfovFeedbackAxis2 = 103,
Automation1PsoWindowInput_GL4AuxiliaryFeedbackAxis1 = 104,
Automation1PsoWindowInput_GL4AuxiliaryFeedbackAxis2 = 105,
Automation1PsoWindowInput_GL4SyncPortA = 106,
Automation1PsoWindowInput_GL4SyncPortB = 107,
Automation1PsoWindowInput_GL4DrivePulseStreamAxis1 = 108,
Automation1PsoWindowInput_GL4DrivePulseStreamAxis2 = 109,
Automation1PsoWindowInput_XL4sPrimaryFeedback = 110,
Automation1PsoWindowInput_XL4sAuxiliaryFeedback = 111,
Automation1PsoWindowInput_XL4sSyncPortA = 112,
Automation1PsoWindowInput_XL4sSyncPortB = 113,
Automation1PsoWindowInput_XL4sDrivePulseStream = 114,
Automation1PsoWindowInput_XR3PrimaryFeedbackAxis1 = 115,
Automation1PsoWindowInput_XR3PrimaryFeedbackAxis2 = 116,
Automation1PsoWindowInput_XR3PrimaryFeedbackAxis3 = 117,
Automation1PsoWindowInput_XR3PrimaryFeedbackAxis4 = 118,
Automation1PsoWindowInput_XR3PrimaryFeedbackAxis5 = 119,
Automation1PsoWindowInput_XR3PrimaryFeedbackAxis6 = 120,
Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis1 = 121,
Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis2 = 122,
Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis3 = 123,
Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis4 = 124,
Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis5 = 125,
Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis6 = 126,
Automation1PsoWindowInput_XR3SyncPortA = 127,
Automation1PsoWindowInput_XR3SyncPortB = 128,
Automation1PsoWindowInput_XR3DrivePulseStream = 129,
Automation1PsoWindowInput_XC4PrimaryFeedback = 130,
Automation1PsoWindowInput_XC4AuxiliaryFeedback = 131,
Automation1PsoWindowInput_XC4SyncPortA = 132,
Automation1PsoWindowInput_XC4SyncPortB = 133,
Automation1PsoWindowInput_XC4DrivePulseStream = 134,
XC4ePrimaryFeedback = 135,
XC4eAuxiliaryFeedback = 136,
XC4eSyncPortA = 137,
XC4eSyncPortB = 138,
XC4eDrivePulseStream = 139,
Automation1PsoWindowInput_XC6ePrimaryFeedback = 140,
Automation1PsoWindowInput_XC6eAuxiliaryFeedback = 141,
Automation1PsoWindowInput_XC6eSyncPortA = 142,
Automation1PsoWindowInput_XC6eSyncPortB = 143,
Automation1PsoWindowInput_XC6eDrivePulseStream = 144,
Automation1PsoWindowInput_XL5ePrimaryFeedback = 145,
Automation1PsoWindowInput_XL5eAuxiliaryFeedback = 146,
Automation1PsoWindowInput_XL5eSyncPortA = 147,
Automation1PsoWindowInput_XL5eSyncPortB = 148,
Automation1PsoWindowInput_XL5eDrivePulseStream = 149,
Automation1PsoWindowInput_XC2PrimaryFeedback = 150,
Automation1PsoWindowInput_XC2AuxiliaryFeedback = 151,
Automation1PsoWindowInput_XC2DrivePulseStream = 152,
Automation1PsoWindowInput_XC2ePrimaryFeedback = 153,
Automation1PsoWindowInput_XC2eAuxiliaryFeedback = 154,
Automation1PsoWindowInput_XC2eDrivePulseStream = 155,
Automation1PsoWindowInput_XL2ePrimaryFeedback = 156,
Automation1PsoWindowInput_XL2eAuxiliaryFeedback = 157,
Automation1PsoWindowInput_XL2eSyncPortA = 158,
Automation1PsoWindowInput_XL2eSyncPortB = 159,
Automation1PsoWindowInput_XL2eDrivePulseStream = 160,
Automation1PsoWindowInput_XI4PrimaryFeedbackAxis1 = 161,
Automation1PsoWindowInput_XI4PrimaryFeedbackAxis2 = 162,
Automation1PsoWindowInput_XI4PrimaryFeedbackAxis3 = 163,
Automation1PsoWindowInput_XI4PrimaryFeedbackAxis4 = 164,
Automation1PsoWindowInput_XI4AuxiliaryFeedback1 = 165,
Automation1PsoWindowInput_XI4AuxiliaryFeedback2 = 166,
Automation1PsoWindowInput_XI4AuxiliaryFeedback3 = 167,
Automation1PsoWindowInput_XI4AuxiliaryFeedback4 = 168,
Automation1PsoWindowInput_XI4SyncPortA = 169,
Automation1PsoWindowInput_XI4SyncPortB = 170,
Automation1PsoWindowInput_XI4DrivePulseStreamAxis1 = 171,
Automation1PsoWindowInput_XI4DrivePulseStreamAxis2 = 172,
Automation1PsoWindowInput_XI4DrivePulseStreamAxis3 = 173,
Automation1PsoWindowInput_XI4DrivePulseStreamAxis4 = 174,
Automation1PsoWindowInput_iXC4PrimaryFeedback = 175,
Automation1PsoWindowInput_iXC4AuxiliaryFeedback = 176,
Automation1PsoWindowInput_iXC4SyncPortA = 177,
Automation1PsoWindowInput_iXC4SyncPortB = 178,
Automation1PsoWindowInput_iXC4DrivePulseStream = 179,
Automation1PsoWindowInput_iXC4ePrimaryFeedback = 180,
Automation1PsoWindowInput_iXC4eAuxiliaryFeedback = 181,
Automation1PsoWindowInput_iXC4eSyncPortA = 182,
Automation1PsoWindowInput_iXC4eSyncPortB = 183,
Automation1PsoWindowInput_iXC4eDrivePulseStream = 184,
Automation1PsoWindowInput_iXC6ePrimaryFeedback = 185,
Automation1PsoWindowInput_iXC6eAuxiliaryFeedback = 186,
Automation1PsoWindowInput_iXC6eSyncPortA = 187,
Automation1PsoWindowInput_iXC6eSyncPortB = 188,
Automation1PsoWindowInput_iXC6eDrivePulseStream = 189,
Automation1PsoWindowInput_iXL5ePrimaryFeedback = 190,
Automation1PsoWindowInput_iXL5eAuxiliaryFeedback = 191,
Automation1PsoWindowInput_iXL5eSyncPortA = 192,
Automation1PsoWindowInput_iXL5eSyncPortB = 193,
Automation1PsoWindowInput_iXL5eDrivePulseStream = 194,
Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis1 = 195,
Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis2 = 196,
Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis3 = 197,
Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis4 = 198,
Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis5 = 199,
Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis6 = 200,
Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis1 = 201,
Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis2 = 202,
Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis3 = 203,
Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis4 = 204,
Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis5 = 205,
Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis6 = 206,
Automation1PsoWindowInput_iXR3SyncPortA = 207,
Automation1PsoWindowInput_iXR3SyncPortB = 208,
Automation1PsoWindowInput_iXR3DrivePulseStream = 209,
Automation1PsoWindowInput_GI4DrivePulseStreamAxis1 = 210,
Automation1PsoWindowInput_GI4DrivePulseStreamAxis2 = 211,
Automation1PsoWindowInput_GI4DrivePulseStreamAxis3 = 212,
Automation1PsoWindowInput_iXC2PrimaryFeedback = 213,
Automation1PsoWindowInput_iXC2AuxiliaryFeedback = 214,
Automation1PsoWindowInput_iXC2DrivePulseStream = 215,
Automation1PsoWindowInput_iXC2ePrimaryFeedback = 216,
Automation1PsoWindowInput_iXC2eAuxiliaryFeedback = 217,
Automation1PsoWindowInput_iXC2eDrivePulseStream = 218,
Automation1PsoWindowInput_iXL2ePrimaryFeedback = 219,
Automation1PsoWindowInput_iXL2eAuxiliaryFeedback = 220,
Automation1PsoWindowInput_iXL2eSyncPortA = 221,
Automation1PsoWindowInput_iXL2eSyncPortB = 222,
Automation1PsoWindowInput_iXL2eDrivePulseStream = 223,
Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis1 = 224,
Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis2 = 225,
Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis3 = 226,
Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis4 = 227,
Automation1PsoWindowInput_iXI4AuxiliaryFeedback1 = 228,
Automation1PsoWindowInput_iXI4AuxiliaryFeedback2 = 229,
Automation1PsoWindowInput_iXI4AuxiliaryFeedback3 = 230,
Automation1PsoWindowInput_iXI4AuxiliaryFeedback4 = 231,
Automation1PsoWindowInput_iXI4SyncPortA = 232,
Automation1PsoWindowInput_iXI4SyncPortB = 233,
Automation1PsoWindowInput_iXI4DrivePulseStreamAxis1 = 234,
Automation1PsoWindowInput_iXI4DrivePulseStreamAxis2 = 235,
Automation1PsoWindowInput_iXI4DrivePulseStreamAxis3 = 236,
Automation1PsoWindowInput_iXI4DrivePulseStreamAxis4 = 237,
# @brief Specifies the PSO output pin settings for each drive.
class Automation1PsoOutputPin:
Automation1PsoOutputPin_GL4None = 100,
Automation1PsoOutputPin_GL4LaserOutput0 = 101,
Automation1PsoOutputPin_XL4sNone = 102,
Automation1PsoOutputPin_XL4sLaserOutput0 = 103,
Automation1PsoOutputPin_XR3None = 104,
Automation1PsoOutputPin_XR3PsoOutput1 = 105,
Automation1PsoOutputPin_XR3PsoOutput2 = 106,
Automation1PsoOutputPin_XR3PsoOutput3 = 107,
Automation1PsoOutputPin_XC4DedicatedOutput = 108,
Automation1PsoOutputPin_XC4AuxiliaryMarkerDifferential = 109,
Automation1PsoOutputPin_XC4AuxiliaryMarkerSingleEnded = 110,
XC4eDedicatedOutput = 111,
XC4eAuxiliaryMarkerDifferential = 112,
XC4eAuxiliaryMarkerSingleEnded = 113,
Automation1PsoOutputPin_XC6eDedicatedOutput = 114,
Automation1PsoOutputPin_XC6eAuxiliaryMarkerDifferential = 115,
Automation1PsoOutputPin_XC6eAuxiliaryMarkerSingleEnded = 116,
Automation1PsoOutputPin_XL5eDedicatedOutput = 117,
Automation1PsoOutputPin_XL5eAuxiliaryMarkerDifferential = 118,
Automation1PsoOutputPin_XL5eAuxiliaryMarkerSingleEnded = 119,
Automation1PsoOutputPin_XC2DedicatedOutput = 120,
Automation1PsoOutputPin_XC2eDedicatedOutput = 121,
Automation1PsoOutputPin_XL2eDedicatedOutput = 122,
Automation1PsoOutputPin_XI4DedicatedOutput = 123,
Automation1PsoOutputPin_iXC4DedicatedOutput = 124,
Automation1PsoOutputPin_iXC4AuxiliaryMarkerDifferential = 125,
Automation1PsoOutputPin_iXC4AuxiliaryMarkerSingleEnded = 126,
Automation1PsoOutputPin_iXC4eDedicatedOutput = 127,
Automation1PsoOutputPin_iXC4eAuxiliaryMarkerDifferential = 128,
Automation1PsoOutputPin_iXC4eAuxiliaryMarkerSingleEnded = 129,
Automation1PsoOutputPin_iXC6eDedicatedOutput = 130,
Automation1PsoOutputPin_iXC6eAuxiliaryMarkerDifferential = 131,
Automation1PsoOutputPin_iXC6eAuxiliaryMarkerSingleEnded = 132,
Automation1PsoOutputPin_iXL5eDedicatedOutput = 133,
Automation1PsoOutputPin_iXL5eAuxiliaryMarkerDifferential = 134,
Automation1PsoOutputPin_iXL5eAuxiliaryMarkerSingleEnded = 135,
Automation1PsoOutputPin_iXR3None = 136,
Automation1PsoOutputPin_iXR3PsoOutput1 = 137,
Automation1PsoOutputPin_iXR3PsoOutput2 = 138,
Automation1PsoOutputPin_iXR3PsoOutput3 = 139,
Automation1PsoOutputPin_GI4None = 140,
Automation1PsoOutputPin_GI4LaserOutput0 = 141,
Automation1PsoOutputPin_iXC2eDedicatedOutput = 143,
Automation1PsoOutputPin_iXL2eDedicatedOutput = 144,
Automation1PsoOutputPin_iXI4DedicatedOutput = 145,
"""
class DriveDataCaptureInput:
PositionCommand = 0
PrimaryFeedback = 1
AuxiliaryFeedback = 2
AnalogInput0 = 3
AnalogInput1 = 4
AnalogInput2 = 5
AnalogInput3 = 6
class DriveDataCaptureTrigger:
PsoOutput = 0
PsoEvent = 1
HighSpeedInput0RisingEdge = 2
HighSpeedInput0FallingEdge = 3
HighSpeedInput1RisingEdge = 4
HighSpeedInput1FallingEdge = 5
AuxiliaryMarkerRisingEdge = 6
AuxiliaryMarkerFallingEdge = 7
class PsoOutputPin:
GL4None = (100,)
GL4LaserOutput0 = (101,)
XL4sNone = (102,)
XL4sLaserOutput0 = (103,)
XR3None = (104,)
XR3PsoOutput1 = (105,)
XR3PsoOutput2 = (106,)
XR3PsoOutput3 = (107,)
XC4DedicatedOutput = (108,)
XC4AuxiliaryMarkerDifferential = (109,)
XC4AuxiliaryMarkerSingleEnded = (110,)
XC4eDedicatedOutput = (111,)
XC4eAuxiliaryMarkerDifferential = (112,)
XC4eAuxiliaryMarkerSingleEnded = (113,)
XC6eDedicatedOutput = (114,)
XC6eAuxiliaryMarkerDifferential = (115,)
XC6eAuxiliaryMarkerSingleEnded = (116,)
XL5eDedicatedOutput = (117,)
XL5eAuxiliaryMarkerDifferential = (118,)
XL5eAuxiliaryMarkerSingleEnded = (119,)
XC2DedicatedOutput = (120,)
XC2eDedicatedOutput = (121,)
XL2eDedicatedOutput = (122,)
XI4DedicatedOutput = (123,)
iXC4DedicatedOutput = (124,)
iXC4AuxiliaryMarkerDifferential = (125,)
iXC4AuxiliaryMarkerSingleEnded = (126,)
iXC4eDedicatedOutput = (127,)
iXC4eAuxiliaryMarkerDifferential = (128,)
iXC4eAuxiliaryMarkerSingleEnded = (129,)
iXC6eDedicatedOutput = (130,)
iXC6eAuxiliaryMarkerDifferential = (131,)
iXC6eAuxiliaryMarkerSingleEnded = (132,)
iXL5eDedicatedOutput = (133,)
iXL5eAuxiliaryMarkerDifferential = (134,)
iXL5eAuxiliaryMarkerSingleEnded = (135,)
iXR3None = (136,)
iXR3PsoOutput1 = (137,)
iXR3PsoOutput2 = (138,)
iXR3PsoOutput3 = (139,)
GI4None = (140,)
GI4LaserOutput0 = (141,)
iXC2DedicatedOutput = (142,)
iXC2eDedicatedOutput = (143,)
iXL2eDedicatedOutput = (144,)
iXI4DedicatedOutput = (145,)

View File

@@ -0,0 +1,140 @@
// 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
//
enum ScanType
Pos = 0
Neg = 1
PosNeg = 2
NegPos = 3
end
program
//////////////////////////////////////////////////////////////////////////
// External parameters - USE THEESE
var $fStartPosition as real = {{ scan.startpos }}
var $fScanRange as real = $fStartPosition + {{ scan.scanrange }}
var $iNumRepeat as integer = {{ scan.nrepeat }}
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
//////////////////////////////////////////////////////////////////////////
// Internal parameters - dont use
var $axis as axis = ROTY
var $ii as integer
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
// 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
MoveAbsolute($axis, $fPosStart, $fVelJog)
WaitForInPosition($axis)
// Configure PSO
PsoDistanceCounterOff($axis)
PsoDistanceEventsOff($axis)
PsoWindowConfigureEvents($axis, PsoWindowEventMode.None)
PsoWaveformOff($axis)
PsoOutputConfigureSource($axis, PsoOutputSource.Waveform)
var $iPsoArrayAddr as integer = 0
// Simple PSO trigger pattern
var $iPsoArrayPos[] as real = [{% for psoDist in scan.psoBoundsPos[:-1] %} UnitsToCounts($axis, {{ psoDist }}), {% endfor %} UnitsToCounts($axis, {{ scan.psoBoundsPos[-1] }})]
// var $iPsoArrayPos[] as real = [UnitsToCounts($axis, $fAccDistance), {% for psoDist in scan.psoBoundsPos[:-1] %} UnitsToCounts($axis, psoDist), {% endfor %}]
// var $iPsoArrayNeg[] as real = [UnitsToCounts($axis, $fAccDistance), {% for psoDist in scan.psoBoundsNeg[:-1] %} UnitsToCounts($axis, psoDist), {% endfor %}]
DriveArrayWrite($axis, $iPsoArrayPos, $iPsoArrayAddr, length($iPsoArrayPos), DriveArrayType.PsoDistanceEventDistances)
PsoDistanceConfigureArrayDistances($axis, $iPsoArrayAddr, length($iPsoArrayPos), 0)
PsoDistanceEventsOn($axis)
PsoWaveformConfigureMode($axis, PsoWaveformMode.Toggle)
PsoWaveformOn($axis)
// Configure Drive Data Collection
var $iDdcArrayAddr as integer = 8388608
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 );
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 ($iNumRepeat-1)
PsoDistanceConfigureArrayDistances($axis, $iPsoArrayAddr, length($iPsoArrayPos), 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
end
Dwell(0.2)
end
// Directly after scan
PsoDistanceCounterOff($axis)
DriveDataCaptureOff($axis, 0)
DriveDataCaptureOff($axis, 1)
end

View File

@@ -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

View File

View File

@@ -0,0 +1,432 @@
import enum
import threading
import time as ttime
from bec_lib.logger import bec_logger
# from typing import Any
from ophyd import ADComponent as ADCpt
from ophyd import Component as Cpt
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
# import numpy as np
from ophyd.ophydobj import Kind
from ophyd_devices.epics.devices.psi_detector_base import CustomDetectorMixin, PSIDetectorBase
# os.environ["EPICS_CA_AUTO_ADDR_LIST"] = "No"
# os.environ["EPICS_CA_ADDR_LIST"] = "129.129.208.143"
logger = bec_logger.logger
class GrashopperError(Exception):
"""Base class for Grashopper detector errors."""
class GrashopperTimeoutError(GrashopperError):
"""Base class for Grashopper detector errors."""
class AutoMode(enum.IntEnum):
"""Acquire time auto for Grashopper detector.
class for acquire_auto and gain_auto
Off: Gain tap balancing is user controlled using Gain.
Once: Gain tap balancing is automatically adjusted once by the device.
Once it has converged, it automatically returns to the Off state.
Continuous: Gain tap balancing is constantly adjusted by the device.
"""
OFF = 0
ONCE = 1
CONTINUOUS = 2
class ImageMode(enum.IntEnum):
"""Image mode for Grashopper detector.
Single: Acquire a single image, ignores NumImages PV
Multiple: Acquire NumImages images
Continuous: Acquire images continuously
"""
SINGLE = 0
MULTIPLE = 1
CONTINUOUS = 2
class DetectorState(enum.IntEnum):
"""Detector states for Grashopper detector"""
IDLE = 0
ACQUIRE = 1
READOUT = 2
CORRECT = 3
SAVING = 4
ABORTING = 5
ERROR = 6
WAITING = 7
INITIALIZING = 8
DISCONNECTED = 9
ABORTED = 10
class ImageBinning(enum.IntEnum):
"""Image binning for Grashopper detector"""
X1 = 1
X2 = 2
X4 = 4
class VideoMode(enum.IntEnum):
"""Video mode for Grashopper detector.
For details, consult EPICs IOC manual.
"""
MODE0 = 0
MODE1 = 1
MODE2 = 2
MODE3 = 3
class PixelFormat(enum.IntEnum):
"""Pixel format for Grashopper detector."""
MONO8 = 0
MONO12PACKED = 1
MONO12P = 2
MONO16 = 3
class COLORMODE(enum.IntEnum):
"""Color mode for Grashopper detector.
Only for readback values from color_mode RO PV.
"""
MONO = 0
BAYER = 1
RGB1 = 2
RGB2 = 3
RGB3 = 4
YUV444 = 5
YUV422 = 6
YUV421 = 7
class TriggerSource(enum.IntEnum):
"""Trigger signals for Grashopper detector"""
SOFTWARE = 0
LINE0 = 1
LINE2 = 2
LINE3 = 3
class MemoryPolling(enum.IntEnum):
"""Memory polling for Grashopper detector.
Defines update rate of memory polling for IOC (1s suggested).
"""
PASSIVE = 0
EVENT = 1
IOINTR = 2
SECONDS10 = 3
SECONDS5 = 4
SECONDS2 = 5
SECONDS1 = 6
SECONDS05 = 7
SECONDS02 = 8
SECONDS01 = 9
class GrashopperTOMCATSetup(CustomDetectorMixin):
"""Mixin class to setup TOMCAT specific implementations of the detector.
This class will be called by the custom_prepare_cls attribute of the detector class.
"""
def __init__(self, *_args, parent: Device = None, **_kwargs) -> None:
super().__init__(*_args, parent=parent, **_kwargs)
self.image_shape = (self.parent.cam.image_size_y.get(), self.parent.cam.image_size_x.get())
self.monitor_thread = None
self.stop_monitor = False
self.update_frequency = 1
self.low_frame_rate = 80
def initialize_detector(self) -> None:
"""Initialize detector."""
self.parent.cam.acquire.put(0)
self.parent.cam.acquire_time_auto.put(AutoMode.CONTINUOUS)
self.parent.cam.gain_auto.put(AutoMode.CONTINUOUS)
self.parent.cam.image_mode.put(ImageMode.MULTIPLE)
self.parent.cam.image_binning.put(ImageBinning.X1)
self.parent.cam.video_mode.put(VideoMode.MODE0)
self.parent.cam.pixel_format.put(PixelFormat.MONO16)
self.parent.cam.trigger_source.put(TriggerSource.SOFTWARE)
self.parent.cam.memory_polling.put(MemoryPolling.SECONDS1)
self.parent.cam.set_image_counter.put(0)
def initialize_detector_backend(self) -> None:
self.parent.image.queue_size.put(5)
self.parent.image.array_port.put(self.parent.cam.port_name.get())
self.parent.image.enable_cb.put(1)
self.parent.image.set_array_counter.put(0)
def set_exposure_time(self, exposure_time: float) -> None:
"""Set the detector framerate.
Args:
framerate (float): Desired framerate in Hz smallest is 87Hz
"""
framerate = 1 / exposure_time
if framerate > self.low_frame_rate:
raise GrashopperError(
f"Trying to set exposure time to {exposure_time}s, this is below the lowest"
f" possible exposure of {1/self.low_frame_rate}s"
)
self.parent.cam.frame_rate.put(framerate)
def prepare_detector(self) -> None:
"""Prepare detector for acquisition."""
self.parent.cam.image_mode.put(ImageMode.MULTIPLE)
self.parent.cam.acquire_time_auto.put(AutoMode.CONTINUOUS)
self.set_exposure_time(self.parent.scaninfo.exp_time)
self.parent.set_trigger(TriggerSource.SOFTWARE)
self.parent.cam.set_image_counter.put(0)
self.set_acquisition_params()
def set_acquisition_params(self) -> None:
"""Set acquisition parameters for the detector"""
# Set number of images and frames (frames is for internal burst of detector)
self.parent.cam.num_images.put(
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger)
)
def prepare_detector_backend(self) -> None:
"""Prepare detector backend for acquisition."""
self.parent.image.set_array_counter.put(0)
self.monitor_thread = None
self.stop_monitor = False
# self.run_monitor()
def arm_acquisition(self) -> None:
"""Arm grashopper detector for acquisition"""
self.parent.cam.acquire.put(1)
signal_conditions = [(self.parent.cam.detector_state.get, DetectorState.WAITING)]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.timeout,
check_stopped=True,
all_signals=False,
):
raise GrashopperTimeoutError(
f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}"
)
def on_trigger(self) -> None:
"""Trigger the detector"""
if self.parent.cam.trigger_source.get() == TriggerSource.SOFTWARE:
self.parent.cam.software_trigger_device.put(1)
ttime.sleep(0.1)
self.send_data()
def run_monitor(self) -> None:
"""
Run the monitor loop in a separate thread.
"""
self.monitor_thread = threading.Thread(target=self.monitor_loop, daemon=True)
self.monitor_thread.start()
def monitor_loop(self) -> None:
"""
Monitor the detector status and send data.
"""
while True:
self.send_data()
ttime.sleep(1 / self.update_frequency)
if self.parent.stopped:
break
def send_data(self) -> None:
"""Send data to monitor endpoint in redis."""
try:
img = self.parent.image.array_data.get().reshape(self.image_shape)
# pylint: disable=protected-access
self.parent._run_subs(sub_type=self.parent.SUB_VALUE, value=img)
except Exception as e:
logger.debug(f"{e} for image with shape {self.parent.image.array_data.get().shape}")
def stop_detector(self) -> None:
"""Stop detector."""
self.parent.cam.acquire.put(0)
signal_conditions = [(self.parent.cam.detector_state.get, DetectorState.IDLE)]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.timeout - self.parent.timeout // 2,
check_stopped=True,
all_signals=False,
):
# Retry stop detector and wait for remaining time
self.parent.cam.acquire.put(0)
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.timeout - self.parent.timeout // 2,
check_stopped=True,
all_signals=False,
):
raise GrashopperTimeoutError(
f"Failed to stop detector, detector state {signal_conditions[0][0]}"
)
def stop_detector_backend(self) -> None:
"""Stop the data backend sending data."""
self.stop_monitor = True
class SLSDetectorCam(Device):
"""
SLS Detector Camera - Grashoppter
Base class to map EPICS PVs to ophyd signals.
"""
## Deprecated PVs, to be checked!
# acquire_time = ADCpt(EpicsSignal, "AcquireTime", kind=Kind.omitted)
# num_exposures = ADCpt(EpicsSignal, "NumExposures", kind=Kind.omitted)
# acquire_period = ADCpt(EpicsSignalWithRBV, "AcquirePeriod", kind=Kind.config)
# Control PVs
acquire_time_auto = ADCpt(EpicsSignal, "AcquireTimeAuto", kind=Kind.config)
frame_rate = ADCpt(EpicsSignalWithRBV, "FrameRate", kind=Kind.normal)
num_images = ADCpt(EpicsSignalWithRBV, "NumImages", kind=Kind.normal)
num_images_counter = ADCpt(EpicsSignalRO, "NumImagesCounter_RBV", kind=Kind.normal)
image_mode = ADCpt(EpicsSignalWithRBV, "ImageMode", kind=Kind.config)
acquire = ADCpt(EpicsSignalWithRBV, "Acquire", kind=Kind.config)
detector_state = ADCpt(EpicsSignalRO, "DetectorState_RBV", kind=Kind.normal)
status_message = ADCpt(EpicsSignalRO, "StatusMessage_RBV", string=True, kind=Kind.config)
set_image_counter = ADCpt(EpicsSignal, "ArrayCounter", kind=Kind.config)
image_counter = ADCpt(EpicsSignal, "ArrayCounter_RBV", kind=Kind.normal)
image_rate = ADCpt(EpicsSignalRO, "ArrayRate_RBV", kind=Kind.omitted)
# Asyn Port name
port_name = ADCpt(EpicsSignalRO, "PortName_RBV", string=True, kind=Kind.omitted)
# Readout related PVs
max_image_size_x = ADCpt(EpicsSignalRO, "MaxSizeX_RBV", kind=Kind.config)
max_image_size_y = ADCpt(EpicsSignalRO, "MaxSizeY_RBV", kind=Kind.config)
image_size_x = ADCpt(EpicsSignalRO, "ArraySizeX_RBV", kind=Kind.config)
image_size_y = ADCpt(EpicsSignalRO, "ArraySizeY_RBV", kind=Kind.config)
# Only BinY PV is working, sets both
image_binning = ADCpt(EpicsSignalWithRBV, "BinY", kind=Kind.config)
gain = ADCpt(EpicsSignalWithRBV, "Gain", kind=Kind.config)
gain_auto = ADCpt(EpicsSignalWithRBV, "GainAuto", kind=Kind.config)
video_mode = ADCpt(EpicsSignalWithRBV, "VideoMode", kind=Kind.config)
pixel_format = ADCpt(EpicsSignalWithRBV, "PixelFormat", kind=Kind.config)
# Desired to set this in future?
color_mode = ADCpt(EpicsSignalRO, "ColorMode_RBV", kind=Kind.config)
# HW Status PVs
temperature_actual = ADCpt(EpicsSignal, "TemperatureActual", kind=Kind.omitted)
# Trigger
trigger_mode_active = ADCpt(EpicsSignalWithRBV, "TriggerMode", kind=Kind.config)
trigger_source = ADCpt(EpicsSignalWithRBV, "TriggerSource", kind=Kind.config)
trigger_delay = ADCpt(EpicsSignalWithRBV, "TriggerDelay", kind=Kind.omitted)
exposure_mode = ADCpt(EpicsSignalWithRBV, "ExposureMode", kind=Kind.omitted)
software_trigger_device = ADCpt(EpicsSignal, "SoftwareTrigger", kind=Kind.config)
# buffer
memory_polling = ADCpt(EpicsSignal, "PoolUsedMem.SCAN", kind=Kind.omitted)
class SLSImagePlugin(Device):
"""SLS Image Plugin
Image plugin for SLS detector imitating the behaviour of ImagePlugin from
ophyd's areadetector plugins.
"""
# Control
array_port = Cpt(EpicsSignal, "NDArrayPort", kind=Kind.omitted, string=True)
enable_cb = Cpt(EpicsSignal, "EnableCallbacks", kind=Kind.config)
queue_size = Cpt(EpicsSignal, "QueueSize", kind=Kind.config)
set_array_counter = Cpt(EpicsSignal, "ArrayCounter", kind=Kind.config)
array_counter = Cpt(EpicsSignal, "ArrayCounter_RBV", kind=Kind.normal)
set_dropped_arrays = Cpt(EpicsSignal, "DroppedArrays", kind=Kind.config)
dropped_arrays = Cpt(EpicsSignal, "DroppedArrays_RBV", kind=Kind.normal)
image_id = Cpt(EpicsSignal, "UniqueId_RBV", kind=Kind.normal)
# Data
array_data = Cpt(EpicsSignal, "ArrayData", kind=Kind.omitted)
# Size related PVs from Plugin
array_size_0 = Cpt(EpicsSignalRO, "ArraySize0_RBV", kind=Kind.omitted)
array_size_1 = Cpt(EpicsSignalRO, "ArraySize1_RBV", kind=Kind.omitted)
array_size_2 = Cpt(EpicsSignalRO, "ArraySize2_RBV", kind=Kind.omitted)
array_dimension_size = Cpt(EpicsSignalRO, "NDimensions_RBV", kind=Kind.omitted)
class GrashopperTOMCAT(PSIDetectorBase):
"""
Grashopper detector for TOMCAT
Parent class: PSIDetectorBase
class attributes:
custom_prepare_cls (GrashopperTOMCATSetup) : Custom detector setup class for TOMCAT,
inherits from CustomDetectorMixin
cam (SLSDetectorCam) : Detector camera
image (SLSImagePlugin) : Image plugin for detector
"""
# Specify which functions are revealed to the user in BEC client
USER_ACCESS = ["describe"]
SUB_MONITOR = "monitor"
SUB_VALUE = "value"
_default_sub = SUB_VALUE
# specify Setup class
custom_prepare_cls = GrashopperTOMCATSetup
# specify minimum readout time for detector
MIN_READOUT = 0
# specify class attributes
cam = ADCpt(SLSDetectorCam, "cam1:")
image = ADCpt(SLSImagePlugin, "image1:")
def stage(self) -> list[object]:
rtr = super().stage()
self.custom_prepare.arm_acquisition()
return rtr
def unstage(self) -> list[object]:
rtr = super().unstage()
self.custom_prepare.stop_monitor = True
return rtr
def set_trigger(self, trigger_source: TriggerSource) -> None:
"""Set trigger source for the detector.
Check the TriggerSource enum for possible values
Args:
trigger_source (TriggerSource): Trigger source for the detector
"""
value = trigger_source
self.cam.trigger_source.put(value)
if __name__ == "__main__":
hopper = GrashopperTOMCAT(name="hopper", prefix="X02DA-PG-USB:", sim_mode=True)
hopper.wait_for_connection(all_signals=True)

View File

@@ -0,0 +1,182 @@
""" Module for Tomcat rotation motors.
The following classes implement the rotation motors for:
- AerotechAutomation1 (Tomcat), based on EpicsMotorIOC.
"""
import threading
import time
import numpy as np
from bec_lib import threadlocked
from ophyd import DeviceStatus
from ophyd_devices.interfaces.base_classes.ophyd_rotation_base import EpicsRotationBase
from ophyd_devices.interfaces.protocols.bec_protocols import BECFlyerProtocol, BECScanProtocol
class TomcatAerotechRotation(EpicsRotationBase, BECFlyerProtocol, BECScanProtocol):
"""Special motor class that provides flyer interface and progress bar."""
SUB_PROGRESS = "progress"
def __init__(
self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
**kwargs,
):
"""Implementation of the Tomcat AerotechAutomation 1 rotation motor class.
This motor class is based on EpicsRotationBase and provides in addition the flyer interface for BEC
and a progress update.
"""
super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self._start_position = None
self._target_position = None
self._stopped = False
self._rlock = threading.RLock()
self.subscribe(self._progress_update, run=False)
# ------------------ alternative to using configure method --------------------- #
@property
def start_position(self) -> float:
"""Get the start position."""
return self._start_position
@start_position.setter
def start_position(self, value: float) -> None:
"""Set the start position."""
self._start_position = value
@property
def target_position(self) -> float:
"""Get the start position."""
return self._target_position
@target_position.setter
def target_position(self, value: float) -> None:
"""Set the start position."""
self._target_position = value
# ------------------ alternative to using configure method --------------------- #
# configure method is optional for flyers within BEC, you can use stage method or pre_scan method to
# set relevant parameters on the device.
# def configure(self, d: dict) -> dict:
# """Configure method from the device.
# This method is usually used to set configuration parameters for the device.
# Args:
# d (dict): Dictionary with configuration parameters.
# """
# if "target" in d:
# self._target_position = d["target"]
# del d["target"]
# if "position" in d:
# self._target_position = d["position"]
# del d["position"]
# return super().configure(d)
def pre_scan(self):
"""Perform pre-scan operation, e.g. move to start position."""
if self._start_position:
self.move(self._start_position, wait=True)
def kickoff(self) -> DeviceStatus:
"""Kickoff the scan.
The kickoff method should return a status object that is set to finish once the flyer flys, and is ready for the next actions.
I would consider the following implementation.
"""
self._start_position = float(self.position)
self.move(self._target_position, wait=False)
status = DeviceStatus(self)
status.set_finished()
return status
def complete(self) -> DeviceStatus:
"""Complete method of the scan.
This will be called in a fly scan after the kickoff, thus, the stage will be moving to it's target position.
It should
The complete method should return a status object that is set to finish once the flyer is done and the scan is complete.
I would consider the following implementation.
"""
threading.Thread(target=self._is_motor_moving, daemon=True).start()
status = DeviceStatus(self)
self.subscribe(status.set_finished, event_type=self.SUB_DONE, run=False)
return status
def stage(self) -> list[object]:
"""Stage the scan.
We add here in addition the setting of the _stopped flag to False for the thread.
"""
self._stopped = False
return super().stage()
def stop(self, success: bool = False) -> None:
"""Stop the scan.
If the device is stopped, the _stopped flag is set to True.
"""
self._stopped = True
super().stop(success=success)
@threadlocked
def _is_motor_moving(self):
"""Function to check if the motor is moving.
This function is used in a thread to check if the motor is moving.
It resolves by running"""
while self.motor_done_move.get():
if self._stopped:
self._done_moving(success=False)
return
time.sleep(0.1)
self._done_moving(success=True)
# TODO This logic could be refined to be more robust for various scan types, i.e. at the moment it just takes
# the start and target position and calculates the progress based on the current position.
def _progress_update(self, value, **kwargs) -> None:
"""Progress update on the scan.
Runs the progress update on the device progress during the scan.
It uses the SUB_PROGRESS event from ophyd to update BEC about the progress.
Scans need to be aware which device progress is relevant for the scan.
Args:
value (float): The value of the motor position.
"""
if (self._start_position is None) or (self._target_position is None) or (not self.moving):
self._run_subs(sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1)
return
progress = np.abs(
(value - self._start_position) / (self._target_position - self._start_position)
)
max_value = 100
self._run_subs(
sub_type=self.SUB_PROGRESS,
value=int(100 * progress),
max_value=max_value,
done=int(np.isclose(max_value, progress, 1e-3)),
)