Script Task Execution

This section lists the APIs to execute and query information for script tasks. The sections document the following aspects:

  • Script task execution
  • Logging API
  • Path resolution
  • Error handling
  • User defined classes and methods
  • User defined script task arguments

Execution Context

Every IScriptTask could be executed, and retrieve passed arguments and other context information. This execution information of a script task is tracked by the IScriptExecutionContext.

The IScriptExecutionContext holds the context of the execution:

  • The script task arguments
  • The current running script task
  • The current active script logger
  • The active project, if existing
  • The script temp folder
  • The script task user defined arguments

The IScriptExecutionContext is also the entry point into every automation API, and provide access to the different API classes. The classes are described in their own chapters like IProjectHandlingApiEntryPoint.

The context is immediately active, when the code block of an IScriptTask is called.

Groovy Code

The client sample illustrates the seamless usage of the IScriptExecutionContext class in Groovy:
scriptTask("taskName", DV_APPLICATION){
  code{  // The IScriptExecutionContext is automatically active here
     // Call methods of the IScriptExecutionContext
     def logger = scriptLogger
     def temp = paths.tempFolder

     // Use an automation API
     generation{
         // Now the Generation API is active
     }
  }
}
In Groovy the IScriptExecutionContext is automatically activated inside the code{} block.

Java Code

For Java clients the method IScriptExecutionContext.getInstance(Class) provides access to the API classes, which are seamlessly available for the groovy clients:
// Java code
// Passed from the script task:
IScriptExecutionContext scriptContext = ...;

// Retrieve automation API in Java
IGenerationApi generation = scriptContext.getInstance(IGenerationApiEntryPoint.class).getGeneration();

// In groovy code it would be:
generation{

}
In Java code the context is always the first parameter passed to every task code (see IScriptTaskCode).

Code Block Arguments

The code block can have arguments passed into the script task execution. The arguments passed into the code{ } block are defined by the IScriptTaskType of the script task.
scriptTask("Task"){
  code{ arg1, arg2, ... -> // arguments here defined by the IScriptTaskType

  }
}

scriptTask("Task2"){
  // Or you could specify the type of the arguments for code completion
  code{ String arg1, List<Double> arg2 ->
  }
}

The arguments can also can be retrieved with IScriptExecutionContext.getScriptTaskArguments().

Task Execution Sequence

The figure shows the overview sequence when a script task gets executed by the user and the interaction with the IScriptExecutionContext. Note that the context gets created each time the task is executed.

Script Task Execution Sequence
Figure 1. Script Task Execution Sequence

User Defined Classes and Methods

You can define your own methods and classes in a script file. The methods a called like any other method.

Using your own defined method
scriptTask("Task"){
    code{
        userMethod()
    }
}

def userMethod(){
    return "UserString"
}

Classes can be used like any other class. It is also possible to define multiple classes in the script file.

Using your own defined class
scriptTask("Task"){
    code{
        new UserClass().userMethod()
    }
}

class UserClass{
    def userMethod(){
        return "ReturnValue"
    }
}

You can also create classes in different files, but then you have to write imports in your script like in normal Groovy or Java code.

The script should be structured as any other development project, so if the script file gets too big, please refactor the parts into multiple classes and so on.

daVinci Block

The classes and methods must be outside the daVinci{ } block.

Using your own defined method with a daVinci block
import static com.vector.cfg.automation.api.ScriptApi.*
daVinci{
    scriptTask("Task"){
        code{}
    }
}

def userMethod(){}

class UserClass{}

Code Completion

Note that the code completion for the Automation API will not work automatically in own defined classes and methods. You have to open for example a scriptCode{} block. The chapterScriptApi describes how to use the Automation API for your own defined classes and methods.

Usage of Automation API in own defined Classes and Methods

In your own methods and classes the automation API is not automatically available differently as inside of the script task code{} block. But it is often the case, that methods need access to the automation API.

The class ScriptApi provides static methods as entry points into the automation API. The static methods either return the API objects, or you could pass a Closure, which will activate the API inside of the Closure.

Access the Automation API like the Script code{} Block

The ScriptApi.scriptCode(Transformer) method provides access to all automation APIs the same way as inside of the normal script code{} block.

This is useful, if you want to call script code API inside of your own methods and classes.

def yourMethod(){
     // Needs access to an automation API
     ScriptApi.scriptCode{
         // API is now available
         generation.generate()
     }
}

The ScriptApi.scriptCode() method can be used to call API in Java style.

def yourMethod(){
     // Needs access to an automation API
     ScriptApi.scriptCode().generation.generate()
}

Java note: The ScriptApi.scriptCode() returns the IScriptExecutionContext.

Access the Project API of the current active Project

The ScriptApi.activeProject() method provides access to the project automation API of the currently active project. This is useful, if you want to call project API inside of your own methods and classes.

def yourMethod(){
     // Needs access to an automation API
     ScriptApi.activeProject{
         // Project API is now available
         transaction{
             // Now model modifications are allowed
         }
     }
}

The ScriptApi.activeProject() method returns the current active IProject.

def yourMethod(){
     // Needs access to an automation API
     IProject theActiveProject = ScriptApi.activeProject()
}

Stateful Script Tasks

Script tasks normally have no state or cached data, but it can be useful to cache data during an execution, or over multiple task executions. The IScriptExecutionContext provides two methods to save and restore data for that purpose:

  • getExecutionData() - caches data during one task execution
  • getSessionData() - caches data over multiple task executions

Execution Data

Caches data during a single script task execution, which allows to save calculated values or services needed in multiple parts of the task, without recalculating or creating it. Note: When the task is executed again the executionData will be empty.

executionData - Cache and retrieve data during one script task execution
scriptTask("TaskName"){
  code{
      // Cache a value for the execution
      executionData.myCacheValue = 500

      def val = executionData.myCacheValue // Retrieve the value anywhere
      scriptLogger.info  "The cached value is $val"

      // Or access it from any place with ScriptApi.scriptCode like:
      def sameValue = ScriptApi.scriptCode.executionData.myCacheValue
  }
}

Session Data

Caches data over multiple task executions, which allows to implement a stateful task, by saving and retrieving any data calculated by the task itself.

Caution: The data is saved globally so the usage of the sessionData can lead to memory leaks or OutOfMemoryErrors. You have to take care not to store too much memory in the sessionData. The DaVinci Configurator will also free the sessionData, when the system run low on free memory. So you have to deal with the fact, that the sessionData was freed, when the script task getting executed again. But the data is not deallocated during a running execution.

sessionData - Cache and retrieve data over multiple script task executions
scriptTask("TaskName"){
  // Setup - set the value the first time, this is only executed once (during initialization)
  sessionData.myExecutionCount = 1

  code{
      // Retrieve the value
      def executionCount = sessionData.myExecutionCount

      scriptLogger.info  "The task was executed $executionCount times"

      // Update the value
      sessionData.myExecutionCount = executionCount + 1
  }
}

API usage

Both methods executionData and sessionData return the same API of type IScriptTaskUserData.

The IScriptTaskUserData provides methods to retrieve and store properties by a key (like a Map). The retrieval and store methods are Object based, so any Object can be a key. The exception are Class instances (like String.class, which required that the value is an instance of the Class).

On retrieval if a property does not exist an UnknownPropertyException is thrown. Properties can be set multiple times and will override the old value. The keys of the properties used to retrieve and store data are compared with Object.equals(Object) for equality.

The listing below describes the usage of the API:

sessionData and executionData syntax samples
scriptTask("TaskName"){
  code{
      def val
      // The sessionData and executionData have the same API

      // You have multiple ways to set a value
      executionData.myCacheId = "VALUE"
      executionData.set("myCacheId", "VALUE")
      executionData["myCacheId"] =  "VALUE"
      // Or with classes for a service locator pattern
      executionData.set(Integer.class, 50)  // Possible for any Class
      executionData[Integer] = 50

      // There are the same ways to retrieve the values
      val = executionData.myCacheId
      val = executionData.get("myCacheId")
      val = executionData["myCacheId"]
      // Or with classes for a service locator pattern
      val  = executionData.get(Integer.class)
      val  = executionData[Integer]

      // You can also ask if the property exists
      boolean exists = executionData.has("myCacheId")
  }
}

ScriptAccess - Calling ScriptTasks

Sometimes it can be helpful to call other script tasks from inside your task. The scripts{} block or getScripts() method provides API to retrieve existing IScripts and call other IScriptTasks from your running IScriptTask.

Note: If you just want to reuse code of your own scripts in an automation script project, create a normal method containing the code and call it, instead of calling the task. The method is typesafe, has code completion support and is much faster than calling a script task.

Calling script tasks

To call a task you need the name of the task and the IScriptTaskType. The IScriptTaskType determines the argument types and the return type of the script task. Then you can use scripts.callScriptTask(String, Object...) to call the script.

You could also use callScriptTaskWithUserArgs(String, String, Object...), if you want to pass user defined arguments.

Call another script task from a script task
scriptTask("TaskName"){
    code{
        scripts.callScriptTask("OtherTask")
        //The same
        scripts{
            callScriptTask("OtherTask")
        }
    }
}

scriptTask("OtherTask"){
    code{
        //Other task code
    }
}

Calling script tasks with task arguments

If the IScriptTaskType requires task arguments, you have to pass the arguments to the callScriptTask() methods. The return value of the method is the returned value of the called script task.

Call another script task with arguments
scriptTask("TaskName", DV_PROJECT){
    code{
        def arg1 = "First argument"
        def arg2 = 5
        def result = scripts.callScriptTask("OtherTask", arg1, arg2)
        // Result contains the calculated value of OtherTask
    }
}

scriptTask("OtherTask"){
    code{arg1, arg2 ->
        return arg1 + arg2
    }
}