Macros

Karabo macros allows to extend the scantool functionality. In this chapter various macros to ease the user experience are described.

Device provided macros

Scantool device, similarly to the device scenes, provides device macros. By right clicking on the device name and selecting Open device macro a dialog with available macros is displayed. After choosing a macro name from the Device Items list a new macro to the project will be added. This can be foreseen as a macro template and user can adjust it to its needs. Currently following macros are available:

pause_scan_step

Macro allows to perform any available scan and perform actions after each step. Macro sets boolean pauseEachStep of Scan environment to True, starts a scan and in the loop waits till scantool goes into PAUSE state. In the pause state user may set attributes or call slots of other karabo devices and resume scan by calling pause slot of scan device.

#############################################################################
# Copyright (C) European XFEL GmbH Schenefeld. All rights reserved.
#############################################################################
from karabacon.cli.scan_client import ScanClient
from karabacon.enums import ScanTypes
from karabo.middlelayer import (
    Bool, Double, Overwrite, Slot, State, String, VectorDouble, VectorInt32,
    VectorString, sleep, waitUntil)


class PauseScanStep(ScanClient):
    """This is an example scan to control the scantool
        Use this macro to keep a static scan in your project
    """
    scanDeviceId = Overwrite(
        displayedName="Scantool Device",
        defaultValue="__KARABO_BACON_DEVICE__")

    scanType = String(
        displayedName="Scan Type",
        defaultValue=ScanTypes.ASCAN.value,
        options=[i.value for i in ScanTypes])

    motors = VectorString(
        displayedName="Motors",
        description="Motor aliases to be used during ascan",
        defaultValue=[])

    sources = VectorString(
        displayedName="Sources",
        description="Aliases of the data sources during ascan",
        defaultValue=[])

    triggers = VectorString(
        displayedName="Triggers",
        description="Aliases of the trigger during ascan",
        defaultValue=[])

    startPositions = VectorDouble(
        displayedName="Start positions",
        defaultValue=[0.])

    stopPositions = VectorDouble(
        displayedName="Stop positions",
        defaultValue=[10.])

    steps = VectorInt32(
        displayedName="Steps",
        defaultValue=[10])

    acqtime = Double(
        defaultValue=0.,
        displayedName="Acquisition Time")

    bidirectional = Bool(
        displayedName="Bidirectional",
        defaultValue=False,
        description="Set this value to true for a snake like mesh scan")

    @Slot(displayedName="Start",
        description="Start a scan",
        allowedStates=[State.PASSIVE])
    async def startScan(self):
        """ Execute a scan with the configured properties
            The scan can be cancelled!
        """

        # Connect to the scan device!
        await self.connect()

        if len(self.sources) > 0:
            await self.select_sources(self.sources)
        if len(self.triggers) > 0:
            await self.select_triggers(self.triggers)

        # use self.scan_device to access scantool directly
        self.scan_device.scanEnv.pauseEachStep = True

        # Do some actions before the scan

        # Start a scan
        success, text = await self._start(
            axis=self.motors, start=self.startPositions,
            stop=self.stopPositions, steps=self.steps,
            scantype=self.scanType, acqtime=self.acqtime,
            bidirectional=self.bidirectional)

        # Get the message from the scan device and check if we scan
        print(f"Scan device information: {text}")
        if success:
            # Wait until the scan is done!
            while self._is_scanning:
                await waitUntil(
                    lambda: self.scan_device.state == State.PAUSED)

                # Do something after each step
                print("Do something after each scan step")

                await self.scan_device.pause()

        # Do some actions after the scan

        # Disconnect from the scan device after run!
        self.disconnect()

    @Slot(displayedName="Stop",
        description="Stop scan",
        allowedStates=[State.PASSIVE, State.ACTIVE])
    async def stopAbsoluteScan(self):
        # Call the internal stop method!
        await self.stop()

Other Macros

Macro to set custom scan pattern

from karabo.middlelayer import Macro, MacroSlot, Hash, setWait

class SetCustomScanPattern(Macro):

    @MacroSlot()
    def execute(self):
        custom_pattern = []
        # Add a row with two positions
        custom_pattern.append(Hash("positions", [0.0, 0.0]))
        # Add 10 rows
        for row in range(10):
            custom_pattern.append(Hash("positions", [row, row * row]))

        setWait("SCANTOOL/DEVICE/ID", "scanEnv.customScanPattern", custom_pattern)