Module Scope

"Scope" refers to the ability of a variable or named module to be seen by calling code. For example, two modules may both declare a variable named "X". X will have a unique value in each module and will refer to a different memory address. Each version of X is local to the scope of the module where it is declared.

A module may declare and use submodules. For example, the Graphics submodule of a typical script application. Those submodules (child modules) will also be able to see and use the variables of the main module (parent module). The parent is within the scope of the child.

However, variables declared in the child modules are not within the scope of the parent and cannot be used by the parent unless directly referenced with a scope resolution operator "\" or "." (Child\Variable or Child.Variable). If a module has two submodules, and each of those has a variable, "X", the parent cannot simply refer to X, but must identify the submodule in which to find a particular version of X. Typically, this is done by creating an object that refers to the instance when launching the child module:

IF SomeCondition;
[
  Handle = Child(); { Launch an instance of Child and retain a handle pointing to it. }
]

You may now refer to variables in that instance of Child using Handle\Variable. If Child also launches submodules, the same rules and techniques apply.

 

If a module needs to access variables or functions of a completely separate module, it is possible to do this by fully describing the scope of the variable or function being called. (This assumes that the variable or module has not been declared as "Protected".) For example, many of the functions described in the Function Reference will include the name of the library module of which they are a member, and provide an example of how they should be called: 

\AlarmManager\Ack(AlarmName, EventTime, Operator);

The following terms are used when describing scope:

Child A submodule is a child of the module whose source file it is part of. Another term for a child module is a "member module".
Parent A parent is a module that contains submodules.
Descendant Submodules may themselves contain their own submodules. A descendant refers to any submodule of a parent.
Ancestor Like "descendant", but looking in the other direction. Starting with any submodule, an ancestor is any parent module up the declaration chain. The root module is an ancestor of every module and every module is a descendant of the root module.
Member Any named object - a variable, constant, module, etc.

Scope Resolution Operators

There are two scope resolution operators: the dot (.) and the backslash (\). The dot operator was added with VTScada version 11.2 and should be used when the intent is to reference a value only within the identified reference scope. If no value is found in that scope, Invalid will be returned. This differs from the backslash operator, which will look for a variable with the matching name in all higher modules if none is found in the reference scope.

There is also a Scope() function, which takes an object reference, a member name (variable or submodule to be found in the object reference), and a Boolean that controls whether the search will be limited to the named reference or if it should search up the scope tree for a match.

The dot operator is equivalent to the function call, Scope(Reference, Member, TRUE). The backslash operator is equivalent to the function call, Scope(Reference, Member, FALSE).

Scope Operators and Late Binding

The scope operators may also be used to accomplish a feature called "late binding". A variable is a name referring to the memory location where a value is stored. The process of "binding" is the means by which the name and the storage location in memory are associated. Late binding (also referred to as "dynamic binding") links a variable or object at run time. Early binding refers to the process of assigning types to variables and expressions at compilation time.

When a scope resolution operator is placed before a variable or module name used in an executable statement, such as in:

\VarName { or  .VarName }

it is considered the equivalent of writing:

Self()\VarName 

but it uses less RAM. Early binding is typically more efficient than late binding because it reduces the amount of time required to set or retrieve a value, whereas late binding consumes more memory, and is slower than a direct variable reference. However, in some instances it is useful to reference modules and variables that are not in scope at compile time, and therefore you must use late binding after they have a chance to start.

Note that if VarName is a protected variable, then Self()\VarName will be Invalid while \VarName will correctly scope to the variable.

 

It is also worth noting the difference between referring to a variable as \VarName versus VarName (without the scope resolution operator). \VarName is late binding while VarName is early/static binding. The former will compile even if there is no VarName in the compile-time static scope. (And, since the example shows the backslash operator rather than the dot operator, will always evaluate to the nearest VarName variable in the run-time scope tree). The latter will only compile if there is a VarName in the compile-time static scope (and will only resolve to that static variable at run-time even if another VarName variable is added dynamically in a nearer scope).