VBScript Functions

Function Signature

Script functions using VBScript are always called with one input array. If the first argument in the array is not empty, then it is a String containing the name of the calling mode. The calling mode is set to register when the script function is supposed to return an array of requested inputs and outputs; it is usually set to vbEmpty to indicate to the script that it is to compute outputs based on any inputs given in the array, though the calling mode may be set to compute. As an example consider the file VB_CalcObject_Eval.vbs, containing the following:

Function VB_CalcObject_Eval ( argArray )
     Dim retVal
     If IsEmpty(argArray(0)) Then
          ' do compute
          retVal = VB_CalcObject_Eval_compute( argArray )
     ElseIf argArray(0) = "register" Then
          retVal = VB_CalcObject_Eval_register()
     ElseIf argArray(0) = "compute" Then
          ' do compute
          retVal = VB_CalcObject_Eval_compute( argArray )
          ' bad call
          retVal = Empty
     End If
     VB_CalcObject_Eval = retVal
End Function

Note that the registration and compute aspects of the VB_CalcObject_Eval function are handled by other VBScript functions: VB_CalcObject_Eval_register and VB_CalcObject_Eval_compute. You can define these functions in the same file—this programming style leads to script readability.

VBScript does not understand the concept of namespaces. This means that if two separate VBScript files that define exactly the same function name are used sometime during the STK session , then the first version of the function is overridden by the second version. Most probably, that type of behavior is not the desired one. To avoid this problem, we highly recommend that all functions and any global variables use the filename as a prefix. That is why the register function for VB_CalcObject_Eval (above) is not named simply "register" (which could be overridden by some other file that defines a register function) but instead "VB_CalcObject_Eval_register" (which is much less likely to be overridden in a different file that obeys this convention).

When the script performs its compute function, the values contained in the input array are in the same order as that in which the Inputs were registered, with the first Input at index 1, the second at index 2, etc., and with the calling mode occupying index 0 of the array.

Registration of Input and Output Arguments

Each script function requests inputs and outputs by returning a descriptor for each of the requested arguments. The descriptor contains a list of strings of the form "keyword = value". STK parses the keyword-value pairs to identify the requested argument. The number of keyword-value pairs that is required to describe an argument depends upon the complexity of the argument: simple inputs and outputs require fewer pairs than more complicated ones. If a requested argument cannot be identified, then that script function is disabled.

The keyword string is case insensitive—we capitalize certain letters for legibility in examples and documentation. The value string is case sensitive however, with some exceptions (e.g., ArgumentType values of "Input" and "Output" are actually case insensitive).

Every requested input or output argument descriptor must contain the keywords ArgumentType and ArgumentName. The value of ArgumentType is either Input or Output. The value of ArgumentName can be any user-specified variable name that obeys the language's syntax for a valid variable name. (Thus, special characters and spaces are not allowed.) The ArgumentName is intended to be a unique name for that argument when referenced in the script.

The order of the keyword-value pairs within an argument descriptor is arbitrary, although we adopt a convention where ArgumentType is first, so that you can identify which arguments are inputs and outputs more readily.

During the registration process, an array of descriptors must be generated and returned. Each descriptor can be either an array of Strings, where each String is a keyword-value pair, or a String containing keyword-value pairs separated by semicolons. An example follows:

Function VB_ForceModel_Eval_register()
     ReDim descripStr(3), argStr(4)
     descripStr(0)="ArgumentType = Output"
     descripStr(1)="Name = Status"
     descripStr(2)="ArgumentName = Status"
     argStr(0) = descripStr
     Dim singleLineDescripStr
     singleLineDescripStr = "ArgumentType = Output ; Name = Acceleration ; "
     singleLineDescripStr = singleLineDescripStr & _
     "RefName = LVLH ; ArgumentName = accel"
     argStr(1) = singleLineDescripStr
     ReDim descripStr(4)
     descripStr(0)="ArgumentType = Input"
     descripStr(1)="Name = Velocity"
     descripStr(2)="RefName = Inertial"
     descripStr(3)="ArgumentName = Vel"
     argStr(2) = descripStr
     argStr(3) = "ArgumentType = Input ; Name = DateUTC ; ArgumentName = Date"
     VB_ForceModel_Eval_register = argStr
End Function

Computing Outputs from Inputs

The order of registration determines the order of the arguments in the incoming and outgoing arrays of the requested data. For example, based on the registration given above, there are 2 requested inputs. When called upon to compute, the input array will then consist of 3 elements:

Index 0 The calling mode, usually vbEmpty
Index 1 The velocity (an array of 3 doubles), referred to by the name Vel in the script
Index 2 The UTC date (a String), referred to as Date in the script.

Accordingly, the output array consists of 2 elements:

Index 0 Status (a String)
Index 1 The acceleration (an array of 3 doubles) relative to the LVLH frame, referred to as accel in the script.

An error results if:

If an error results, the script is turned off, and a message explaining the problem is output.

If the script function returns a String instead of an array, an error results which then will cause the script to be turned off. Additionally, the returned string will be written to the Message Viewer window. Thus, the script has the ability to turn the script off and indicate a reason for doing so, simply by returning a String.

Accessing Data on the Basis of ArgumentName

Because the order of registration implies the order of the data in the input array and the output array, it is imperative that the correct association between the argument and its index number be used. Thus, adding new arguments in the middle of the current list or even just re-ordering the list of descriptors in the register function will require modifications of the index numbers used in the compute function. To alleviate this burden somewhat, you can choose to use auxiliary classes that hold the association between argument and index number. This allows you to access data by name, which should result in fewer modifications being necessary in the compute function when the registration function is altered.

After the registration process occurs, STK makes an additional call to VBScript to create two Classes (one for inputs, one for outputs) that can be used to access the data by ArgumentName (rather than using index numbers). These classes can be created and stored during the first call to compute, and then accessed by compute thereafter. The classes are created by calling the function g_GetPluginArrayInterface. For example, consider the function VB_ForceModel_Eval. Its compute function would look like this:

Function VB_ForceModel_Eval_compute( stateData )
     ' This function ASSUMES that VB_ForceModel_Eval_register will set
     ' VB_ForceModel_Eval_globalVar to -1
     If VB_ForceModel_Eval_globalVar < 0 Then
       ' If one were to uncomment certain statements below, then a description of 
       ' the Inputs and the Outputs for this function would be popped up in a 
       ' MsgBox and the user would need to hit 'OK'
       'Dim outStr
       Set VB_ForceModel_Eval_Inputs = _
         'outStr = VB_ForceModel_Eval_Inputs.Describe()
       'MsgBox outStr
        Set VB_ForceModel_Eval_Outputs = ¬_
        'outStr = VB_ForceModel_Eval_Outputs.Describe()
        'MsgBox outStr
        ' MAKE sure this If-Then block is executed only once 
        ' (unless VB_ForceModel_Eval_globalVar was reset to -1 by
        ' VB_ForceModel_Eval_register
        VB_ForceModel_Eval_globalVar = 1
     End If
     ' Declare some temporary variables
     Dim factor, cbiVel, cbiSpeed, dateString
     dateString = stateData(VB_ForceModel_Eval_Inputs.Date)
     factor = 0.000001
     cbiVel = stateData(VB_ForceModel_Eval_Inputs.Vel)
     cbiSpeed = sqr(cbiVel(0)*cbiVel(0)+cbiVel(1)*cbiVel(1)+cbiVel(2)*cbiVel(2))
     Redim accelValue(3)
     accelValue(0) = 0.0
     accelValue(1) = factor*cbiSpeed
     accelValue(2) = 0.0
     ' Declare an array of the proper size
     Dim returnValue(2)
     'Assign output values
     returnValue(VB_ForceModel_Eval_Outputs.accel) = accelVal
     returnValue(VB_ForceModel_Eval_Outputs.Status) = "Still Okay"
     VB_ForceModel_Eval_compute =  returnValue
End Function

The variables VB_ForceModel_Eval_globalVar, VB_ForceModel_Eval_Inputs and VB_ForceModel_Eval_Outputs are global variables and must be declared by a Dim statement outside of any function. During VB_ForceModel_Eval_register, VB_ForceModel_Eval_globalVar should be set to -1. The first time VB_ForceModel_Eval_compute is called, the If-Then statement is executed, causing two classes to be created (VB_ForceModel_Eval_Inputs and VB_ForceModel_Eval_Outputs). Because they have been declared global, they are remembered for the next call to compute.

Note that the inputs are accessed from stateData using the class VB_ForceModel_Eval_Inputs and the ArgumentName that was registered. Similarly, the outputs are assigned into the returned array using the class VB_ForceModel_Eval_Outputs and the ArgumentName that was registered. The classes provide a mechanism in the script to access the inputs and outputs by name, rather by index number. Of course, they do this by mapping the ArgumentName to an index value:

Name Index Value
VB_ForceModel_Eval_Inputs.Vel 1
VB_ForceModel_Eval_Inputs.Date 2
VB_ForceModel_Eval_Outputs.Status 0
VB_ForceModel_Eval_Outputs.accel 1

(Remember, on input the argument at index 0 is the calling mode.)

A matrix is passed in VBScript as a single array, containing the rows of the matrix in order, not in the form of an array of arrays.

You must be careful to respond to all message boxes initiated by VBScript. Suppose a computation is started that uses a VBScript function that pops up a message box. If, instead of responding to the message, you click the Cancel button on the progress bar to stop the computation, the message box may be left stranded; dismissing it or calling a VBScript function again can cause STK to crash. Before using the cancel button, be sure to dismiss all popped up messages

STK 11.2.1 Programming Interface