States and Steady State
The executable code of a module must be contained within one or more named states. A state is a named block of executable code.
A module may contain many states but only one state in a module can be active at any time. Code in other states does not run until execution is passed from one state to another. You have seen this in action when configuring VTScada tags. Each tab within the configuration dialog represents a separate state. When you open a tab, execution changes to the matching state, drawing the contents for that tab. The content of the other tabs / states is not seen because those states are not active.
The Connection state is active.
States for ID, TLS and Display are not and therefore are not seen.
State Naming Rules
All states must have a unique name. The name may be the same as the module's name.
- State names must be a single word with no spaces.
- Use an underscore or PascalCase (also called UpperCamelCase) to indicate multiple words
- State names may not include most symbols, such as #&-+=<>{}[](), etc.
- State names are not case-sensitive. "Main" is the same as "main".
- State names may be numeric, so long as you do not include a negative sign.
In general, state names should be:
- Short
- Descriptive - of the state's purpose
- Verbs - when the state performs a task
You will often see modules that contain states named "Init" and "Main". There is no functional significance to these names, but by custom, Init is used for initialization tasks and Main is used for the state that forms the main body of the module.
State Structure
A state is defined using a formal structure as follows. Note the square brackets that enclose the body of the state.
StateName [ Statement1 { a line of executable code }; Statement2 { another line of code }; ]
Order of execution - Steady State
When a module starts, the first state within that module will become active and each statement within it will run once. The order of the statements is relevant only for page and widget modules, where the last statement drawn appears on top of the graphics from earlier statements. This can be seen by examining the code of a page before and after changing the order of graphics in the Idea Studio. If you send something to the back, its statement will be shuffled to the beginning of the state. If you send something to the front, its statement will be shuffled to the end of the state.
After the initial run-through, and assuming that control is not then passed to another state, the currently active state enters a mode termed "steady-state". In essence, this is a form of event-driven programming and results in VTScada being extremely efficient. If a variable changes in value (perhaps a tag records a new value) then statements containing that variable will execute, but no others. This does not change the display order of graphics.
The module may contain other states, but these will never run until control is passed to them. When that happens, the previously running state ends, along with all submodules that were called into that state.
Other than for the display order of graphics, the order of statements within a state does not matter. For example, given a script application with the following code in its Graphics() submodule:
Graphics ( X = 2 { value to display }; ) Main [ X = 4; ZText(100, 100, Concat("The value of X is ", X), "<FFFFFFFF>", 0); ] { End of System\Graphics } >
The message will read, "The value of X is 4".
But, if you change the order of the statements in Main as follows:
Main [ ZText(100, 100, Concat("The value of X is ", X), "<FFFFFFFF>", 0); X = 4; ]
The message will still read, "The value of X is 4". It will not display 2, as you might have expected.
Tracing the order of execution in the second example, what happens is the following:
- The ZText displays the initial value of X, which is 2.
- X gets set to 4.
- This triggers the statement containing X as a variable to run again.
- The message now displays 4.
When working in steady-state, never write code that depends on statements running in a given order. You cannot predict the order of execution.
Infinite Loops in Steady State
Consider the following example:
Graphics ( X = 2 { a variable }; Y = 4 { another variable }; ) Main [ X = Y + 1; Y = X + 1; ] { End of System\Graphics } >
Each statement changes the value of a variable in the other statement, triggering that statement to run again. This is essentially an infinite loop and will "peg" a core of your CPU.
Similarly, the statement:
X = X + 1;
creates an infinite loop when written in steady-state, with X increasing continuously.
Double-Set Values
Because it is not possible to predict the order of execution of statements in steady-state, VTScada does not allow you to write two statements that both set the value of the same variable. For example:
Main [ X = 1; X = 2; ]
This will compile and run, but the value of X will be Invalid rather than 1 or 2. Examined in the Source Debugger, it will appear as "double set", informing you of why it is Invalid. Whenever you have a variable that is set to Invalid when you are sure that it should have a value, look first for a double-set.
There is another way to write a double-set, which to the untrained eye does not look like a double set. Refer to Double-Sets with Called Modules:.
Functions and Steady-State
When selecting functions for use in steady-state code, take note of the "usage" description. If the usage is described as "script only", it cannot be used in steady-state.
Changing States
To move focus from one state to another, you may use either an action trigger or a ForceState() function. Action triggers are the more common of the two.
Build a Simple Script Application
If you have Script 1, from an earlier example, use that and start with step 9. Otherwise, begin with step 1.
- In the VAM, click the Add Application Wizard button.
- Select the Advanced option and click Next.
- Select "Create New" and click Next.
- Set the name to "Script 1".
- Set the type to "Script Application".
- Click Next.
- Deselect the Start Application Now button.
Do not start the application until after the next set of steps. - Click, Finish.
Check that the icon matches. If not, you missed step 5.
- Use a text editor to open the file,
C:\VTScada\Script1\AppRoot.SRC. - Examine this file, in light of what you have learned so far about comments, variables, states and modules.
- Identify the start, the name and the end of the submodule.
- Identify all states.
- Identify all variable declarations.
- There is one function call: Window(). Open the documentation for this function and compare the parameters listed there to what you see in AppRoot.SRC. In particular, note the relation of the submodule, Graphics, to the function call, Window
- Modify the code as follows. (Changes begin at about line 36.)
< {======================== System\Graphics ==========================} { This module handles all of the graphics for the application } {===================================================================} Graphics [ X { Input value }; Color { Color to use }; ] Main [ { Enter a value from 0 to 9 using the keyboard } X = Keys(1, 1); { Check for initial null and substitute a value } Color = X == "" || !Valid(X) ? 7 : X; { Display greeting with selected color } ZText(100, 100, "Hello World", Color, 0); ] { End of System\Graphics } >
- Save your work, then in the VAM, click the Import File Changes button.
- Provide a comment when prompted.
- Run the application.
- Tap the number keys from 0 to 9.
This demonstrates steady state. Each key press is caught by the Keys function and changes the value of X. Each change to the value of X causes the ZText function to refresh.