User Code

User Defined Classes and Methods

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

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

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

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

Use a scriptCode{} block for Automation API code completion in own classes. See the next chapter for details.

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.

ScriptApi.scriptCode{} usage in own method
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.

ScriptApi.scriptCode() usage in own method
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(Closure) 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.

ScriptApi.activeProject{} usage in own method
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.

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

User Defined Script Task Arguments

A script task can create IScriptTaskUserDefinedArgument, which can be set by the user (e.g. from the commandline) to pass user defined arguments to the script task execution. An argument can be optional or required. The arguments are type safe and checked before the task is executed. An argument can be specified with a value and also without one.
Example: "--count 25" or "-s"

Possible valueTypes are:

  • String
  • Boolean
  • Void: For parameter where only the existence is relevant.
  • File: The existence of the file is not checked by default. See argument validators.
  • Path: Same as File
  • Integer
  • Long
  • Double

The help text is automatically expanded with the help for user defined script task arguments.

Script task UserDefined argument with no value
scriptTask("TaskName"){
  def procArg = newUserDefinedArgument("p", Void, "Enables the processing of ...")
  code{
      if(procArg.hasValue){
        scriptLogger.info  "The argument -p was defined"
      }
  }
}
Define and use script task user defined arguments from CLI
scriptTask("TaskName"){
  def countArg = newUserDefinedArgument("count", Integer,
                                        "The amount of elements to create")

  def nameArg = newUserDefinedArgument("name", String,
                                       "The element name to create")
  code{
      // NOTE: The value can only be retrieved within the code closure
      int count = countArg.value
      String name = nameArg.value

      scriptLogger.info  "The arguments --name and --count were $name, $count"
  }
}
Script task UserDefined argument with default value
scriptTask("TaskName"){
  //  User Defined Argument with the default value 25.0
  def procArg = newUserDefinedArgument("p", Double, 25.0, "Help text ...")
  code{
      double value = procArg.value
      scriptLogger.info  "The argument -p was $value"
  }
}
Script task UserDefined argument with multiple values
scriptTask("TaskName"){
  def multiArg = newUserDefinedArgument("multiArg", String, "Help text ...")

  code{

      List<String> values = multiArg.values  // Call values instead of value
      scriptLogger.info  "The argument --multiArg  had values: $values"
  }
}

User defined Argument Validators

You could also specify a validator for the argument to check for special conditions, like the file must exist. This is helpful to provide a quick feedback to the user, if the task would be executable. Simply add the validator at the end of the newUserDefinedArgument() call. The validator code is called when the input is checked. There are also default validators available, like:
  • Constraints.IS_EXISTING_FOLDER
  • Constraints.IS_EXISTING_FILE
  • Constraints.IS_VALID_AUTOSAR_SHORT_NAME
Please see chapter Constraints for more available validators.
Script task UserDefined argument with predefined validator
import  com.vector.cfg.util.contract.util.Constraints

scriptTask("TaskName"){
  def contArg = newUserDefinedArgument( "p", String,
                                        "Help text ...",
                                        Constraints.IS_VALID_AUTOSAR_SHORT_NAME_PATH )
  code{

      String value = contArg.value
      scriptLogger.info  "The argument -p was $value"
  }
}

Or you implement your own validation logic, by passing a Closure, which throws an exception, if the value is invalid.

Script task UserDefined argument with own validator
scriptTask("TaskName"){

  //  User Defined Argument with the validator code as parameter
  newUserDefinedArgument( "p", Integer, 20, "Help text ...",
                { value ->
                    if( value % 2){
                        throw new IllegalArgumentException("The value has to be even.")
                    }
                } )

  code{
  }
}

Constraints

Constraints provides general purpose constraints for checking given parameter values throughout the automation interface. These constraints are referenced from the AutomationInterface documentation wherever they apply. The AutomationInterface takes a fail fast approach verifying provided parameter values as early as possible and throwing appropriate exceptions if values violate the corresponding constraints.

The following constraints are provided:

IS_NOT_NULL

Ensures that the given Object is not null.

IS_NON_EMPTY_STRING

Ensures that the given String is not empty.

IS_VALID_FILE_NAME

Ensures that the given String can be used as a file name.

IS_VALID_PROJECT_NAME

Ensures that the given String can be used as a name for a project. A valid project name starts with a letter [a-zA-Z] contains otherwise only characters matching [a-zA-Z0-9_-] and is at most 128 characters long.

IS_NON_EMPTY_ITERABLE

Ensures that the given Iterable is not empty.

IS_VALID_AUTOSAR_SHORT_NAME

Ensures that the given String conforms to the syntactical requirements for AUTOSAR short names.

IS_VALID_AUTOSAR_SHORT_NAME_PATH

Ensures that the given String conforms to the syntactical requirements for AUTOSAR short name paths.

IS_ABSOLUTE

Ensures that the given Path is absolute.

IS_WRITABLE

Ensures that the file or folder represented by the given Path exists and can be written to.

IS_READABLE

Ensures that the file or folder represented by the given Path exists and can be read.

IS_EXISTING_FOLDER

Ensures that the given Path points to an existing folder.

IS_EXISTING_FILE

Ensures that the given Path points to an existing file.

IS_CREATABLE_FOLDER

Ensures that the given Path either points to an existing folder which can be written to or points to a location at which a corresponding folder could be created.

IS_DCF_FILE

Ensures that the given Path points to a DaVinci Developer workspace file (.dcf file).

IS_DVJSON_FILE

Ensures that the given Path points to a DaVinci project file (.dvjson file).

IS_ARXML_FILE

Ensures that the given Path points to an .arxml file.

Run Script Task with User Defined Task Arguments from CLI

The help of the run command shows, how to execute a script task with user defined arguments.

Help for run script task
Usage: dvcfg-b automation run [-h] -b=<folder> [-p=<file>]
                  -t=<task>[,<task>...] [-t=<task>[,<task>...]]...
                  [-a=<arg>]... [-l=<location>[,<location>...]]...
                  [--debugger[=<port>]] [--no-save]

Description:
Run automation tasks with arguments.
Help for user defined task arguments
* -t, --task=<task>[,<task>...]    List of script tasks to execute.
                                   E.g.: -t task1,task2
 -a, --arg=<arg>                   Define a set of arguments specific to a single task.
                                   E.g.: -a 'task1' -a '--name=str1 --value=1' -a 'task2' -a '-i 1,2,3'

Let’s have a look at an example script task with user defined arguments as shown below.

Example script task with user defined arguments
import static com.vector.cfg.automation.api.ScriptApi.*

scriptTask("userArgTask", DV_APPLICATION) {

def arg_enable = newUserDefinedArgument("enable", Void, "Help text ...")
def arg_count = newUserDefinedArgument("count", Integer, "Help text ...")
def arg_name = newUserDefinedArgument("name ", String, "Help text ...")
def arg_double = newUserDefinedArgument("double", Double, 25.0, "Help text ...")
def arg_multiArg = newUserDefinedArgument("multiArg", String, "Help text ...")

code {
    if (arg_enable.hasValue) {
        scriptLogger.info "The argument --enable was defined."
    }
    if (arg_count.hasValue) {
        scriptLogger.info "The argument --count has the value ${arg_count.value}."
    }
    if (arg_name.hasValue) {
        scriptLogger.info "The argument --name has the value ${arg_name.value}."
    }
    if (arg_double.hasValue) {
        scriptLogger.info "The argument --double has the value ${arg_double.value}."
    }
    if (arg_multiArg.hasValue) {
        List<String> values = arg_multiArg.values
        scriptLogger.info "The argument --multiArg has values: ${values}."
    }
    }
}

To run this script task from CLI, you can use the following command.

Example CLI call with user defined task arguments
automation run ...
           -t "userArgTask"
           -a "userArgTask"
           -a "--enable --count=25 --name=John --double=25.0 --multiArg=1,2,3,4,5"

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