Including Looping in Scripts

The script DSL supports a variety of looping constructs to satisfy different use cases:

  1. loop
  2. waitFor
  3. loopAsync

loop - With Default loop Interval

The loop DSL allows you to start a “blocking” loop so that the rest of the code after loop will not be executed until a stopWhen condition written inside loop becomes true. You can use this DSL when you want to iteratively perform some actions until a certain condition becomes true. An interval can be provided to set the minimum period of the loop, where every iteration of the loop wait at least for the minimum provided interval before executing the next iteration. If no period is provided, the default interval is 50 milliseconds.

The following example demonstrates the usage of the loop DSL with the default interval. In the loop body, a motor is being “moved” by 10 degrees in every iteration of the loop. The loop will be terminated when motor’s current position reaches the expected position of 100 degrees.

Kotlin
sourcevar motorPosition = 0
fun moveMotor(degrees: Int) {
    // move motor logic
    motorPosition += degrees
}
onSetup("move-motor") {

    val expectedMotorPosition = 100

    // move motor by 10 degrees in each iteration, default loop interval is 50 millis
    // stop loop when current motor position matches expected motor position and continue with the execution of rest of the handler
    loop {
        moveMotor(10)
        stopWhen(motorPosition == expectedMotorPosition)
    }
}

loop - With Custom Minimum Loop Interval

The following example demonstrates the usage of the loop DSL when providing a custom loop interval.

Kotlin
sourceonSetup("move-motor") {

    val expectedMotorPosition = 100
    // move motor by 20 degrees in every iteration after a loop interval of 500 millis (custom loop interval used here)
    // stop loop when current motor position matches expected motor position and continue with the execution of rest of the handler
    loop(minInterval = Duration.milliseconds(500)) {
        moveMotor(20)
        stopWhen(motorPosition == expectedMotorPosition)
    }
}
Is there a minimum loop interval?

minInterval needs to be greater than default interval of 50 milliseconds otherwise it will be ignored and default loop interval will be used.

waitFor - Loop Until a Condition is True

This is a specialized version of loop and satisfies simple use cases where you want to semantically block the execution until certain condition becomes true.

In the following example, initializeMotor method will start the initialization and eventually set motorUp flag to true indicating motor is successfully initialized. waitFor { motorUp } will check the value of motorUp flag every 50 milliseconds, and when it is true, the rest of the code execution will continue.

Kotlin
sourcevar motorUp = false

fun initializeMotor() {
    // some motor initialization logic goes here
    motorUp = true
}
onSetup("init-motor") {
    // start initializing motor and this method will set motorUp flag to true once initialization is successful
    initializeMotor()
    // pauses the init-motor command handlers execution until motor becomes up
    waitFor { motorUp }

    // rest of the handler implementation (here you can safely assume that motor is up)
}

loopAsync - With a Default Loop Interval

The previous DSL loop constructs “block” until the loop completes. You can use this DSL when you want to iteratively perform some actions in the background. Since it is asynchronous, once the code in the background loop starts, the code written after loopAsync will be executed immediately and concurrently.

Like loop, loopAsync will be terminated when a stopWhen condition written inside loop becomes true. loopAsync also has a default interval of 50 milliseconds.

The following example demonstrates the usage of loopAsync DSL with the default interval. In the loop body, a current temperature event is published every 50 milliseconds. The loopAsync will be terminated when stopPublishingTemperature flag becomes true, which is set to true in the onStop handler.

Kotlin
sourcevar stopPublishingTemperature = false
val temperatureEvent = SystemEvent("IRIS.motor", "temperature")
val temperatureKey = longKey("temperature")

fun getCurrentTemp(): Long = TODO()

// start background loop which publishes current temperature of motor every 50 milliseconds (default loop interval)
loopAsync {
    val currentTemp = getCurrentTemp()
    publishEvent(temperatureEvent.add(temperatureKey.set(currentTemp)))
    stopWhen(stopPublishingTemperature)
}

onStop {
    stopPublishingTemperature = true
}

loopAsync - With a Custom Loop Interval

The following example demonstrates the usage of loopAsync DSL with a custom loop interval specified.

Kotlin
source// start background loop which publishes current temperature of motor every 100 milliseconds
loopAsync(minInterval = Duration.milliseconds(100)) {
    val currentTemp = getCurrentTemp()
    publishEvent(temperatureEvent.add(temperatureKey.set(currentTemp)))
    stopWhen(stopPublishingTemperature)
}

Source code for examples