Understanding How Scripts Operate in InTouch

This describes how the script engine operates within the Wonderware® InTouch™ environment. This is intended for all Wonderware employees and distributors, OEMs, VARs, MIS/MES professionals, system integrators, application engineers, system developers, and system administrators of InTouch applications. You should have an advanced programming competency level in order to apply some of the features shown in this issue.

The testing for this article was completed using InTouch Release 5.6 under Microsoft Windows 95 ; however the results should be accurate for InTouch releases 5.0b through 5.6.

Disclaimer The information contained here is correct based on the scripting examples and demonstration application that were used in this article. However, your results and experiences in using the InTouch script engine may vary due to circumstances outside the realm of the examples that are shown here.

Installing the Associated InTouch Test Applications

This article references two InTouch test applications that demonstrates the various points made in this article. These applications will run under InTouch Release 5.6 or later .

  • Use the Testscr1.zip file for the InTouch Test Script application #1.
  • Use the Testscr2.zip file for the InTouch Test Script application #2.

The InTouch script engine encompasses and operates a number of script types in a seamless manner. Programmers may occasionally encounter problems with complex scripts, especially when they execute one type of script from another. This Tech Note will help you think out your scripts and understand how the script engine works so that when you write your scripts, you will do so with minimal errors.

Writing Scripts

The easiest way to write an InTouch script—especially if it is complex—is to first flowchart what you want to accomplish. However, keep in mind that your flowchart cannot have any loops in it. This type of flowcharting is called top-down or flow-through flowcharting. The advantage of flowcharting is that if you can flowchart your application, then you will be able to script it, no matter how complex. Also, the flowchart does not need to be overly complex or detailed; it is simply your roadmap, or plan, to keep you on the right track.

Initial Script Operation

After you flowchart your application, the next step is to decide which scripts will control each process in your flow chart. If there are no timed or scanned scripts (that is, While True, While Showing, and so on), when WindowViewer starts up, the script engine will execute the scripts in the order shown in Table 1.

Table 1. InTouch Script Engine Order of Execution

Figures 1 and 2 below comprise a simplified flowchart of how the script engine operates that is accurate for all scripting questions that you may have. However, note that the interrupt vector paths are not shown and they may occur at any point in any script. Keep in mind that whenever a timer completes (each timer is checked for completion just after the Tick Interval is reset), the flowchart will branch back to the script subroutine instance which called the interrupt and complete or halt on another timer, then return.

Figure 1. Scripting Flowchart – Main Program Body (Note: AppTick is the Application Tick Interval)

Figure 2. Scripting Flowchart – Script Subroutine

The unscanned Application On Startup script is run first. Data Change or Condition scripts may not execute from an Application On Startup script.

Warning If you try to execute a Data Change or Condition script from an Application On Startup script, then the data that you are changing may not yet be available globally. This is because the InTouch runtime variable database is initializing while the Application On Startup script is executing. Technical Support’s tests show that in some cases, a Data Change or Condition script may actually run, however, this may not be true every time Window Viewer is restarted.

If a window is called from an Application On Startup script, then the window’s processes (including any Window Action scripts that are called) will not start until the Application On Startup script completes. However, these requests will be queued and run once the Application On Startup script and the home window’s On Show script complete.

The home window’s Window Action On Show scripts run after WindowViewer successfully starts up. If these scripts change any data, then any unscanned Data Change scripts are executed first, and the unscanned Condition scripts are executed next. All scanned scripts are handled in this same manner.

The difference between the unscanned and scanned scripts is that the scanned scripts are put into a storage area during the scan time, and the remaining unscanned scripts within the scanned script shell or thread will not run until the script scan time completes. (Note that this type of script design allows “nesting” of scripts operating within other scripts.) Once the scan time expires, this causes an interrupt vector in the current script. The interrupt branches to the timed out scanned script which runs either to completion or until another scanned script executes.

Timed out scripts do not run when the script timer completes. Remember that many processes are running at any given moment, and some of these processes cannot be interrupted. Examples of processes that cannot be interrupted are disk writes, DDE updates, ODBC, and preconditioned processes in InTouch (that is, FOR-NEXT loops, other scripts with senior precedence, and so on). When the scan time expires, a flag that prevents the script from running while the timer is counting is cleared, allowing the timed script to begin. A script with a scan time of 100 milliseconds would probably run once every 100 milliseconds if there was no I/O Server running or historical logging or retrieval, and if the application had only one simple window with no other scripts running. However, this is unrealistic for “real world” applications!

On Shutdown Application scripts run when WindowViewer is shutting down. If you try to execute a Data Change script and/or a Condition script from an On Shutdown script, then the Data Change and Condition scripts may not be called or complete. Like the On Startup scripts, the Data Change and Condition script subroutines in the script engine may not be available during shutdown. If you are trying to achieve some internal process or condition before WindowViewer exits, your best chance of achieving that process or condition is to execute the Data Change or Condition script before the On Shutdown script. When the On Shutdown script is running, processes within WindowViewer are being shut down. Under certain circumstances, “long” processes will not finish if WindowViewer shuts down before the task completes that were executed from the On Shutdown script.

Tick Interval and Script Timing

The Tick Interval is configured in WindowMaker under the menus Special / Configure / View Generic / Tick Interval . This setting is the master application tick for all running scripts. If you have a Tick Interval of 1000 milliseconds and your timed scripts running within the script engine are set for 100 milliseconds, then they will not run at least until the Tick Interval has been reset. The result is that each timed script will not run for at least 1000 milliseconds even if they are set for a smaller value. Note that these times are not cumulative. That is, if you use a timed script, the timer will run asynchronously with the Tick Interval timer.

What happens if the Tick Interval is set for a smaller value than your timed script? For example, if the Tick Interval is set to 100 milliseconds and a Condition script set to 450 milliseconds, then when will the Condition script run? Assuming no “long” processes are running that take more than a few milliseconds, once the Condition script begins and the timer is running, the Tick Interval will reset five times before the Condition script times out. This means the script will run at about 500 milliseconds, not 450 milliseconds.

Within the InTouch application, the “outside” loop of the script engine (as shown in Figure 1) has the lowest priority. This is so other Windows processes can continue to run. These Windows processes will try to run at 55 milliseconds under Windows 95 or Windows for Workgroups, or 10 milliseconds under Windows NT; this is the OpSysTick . DDE processes are serviced at this rate, then are redefined at the scan time set in the I/O Server, if one is running. The “operating system” screen updates are handled at this rate, as well as the keyboard and mouse “operating system” scans.

Invoked Immediate Scripts *

(* “Immediate” is a programming term that is used to describe the current or immediate program process thread.)

An Invoked Immediate script is a script that is called from any process that is currently in focus. This does not necessarily depend on the Tick Interval or the main script engine thread. Two examples of an Invoked Immediate script are: 1) calling a Data Change script that is triggered by a DDE update (that is, a I/O Server updates a value in the InTouch runtime database); and 2) running a Pushbutton or Key script. These Data Change, Pushbutton, and Key scripts run immediately until they complete. Note that all other processes are halted until these scripts complete or until they start a timer (which allows for an interrupt vector) so that InTouch can branch to other processes.

An interesting effect is how Immediate windows are opened. If, for instance, a Pushbutton script calls a popup or overlay window whose Window Action script then calls another popup or overlay window, then in what order are the scripts executed? The Pushbutton script will run the script subroutine shown in Figure 2 which will run a Show() window function that is a line in the Pushbutton script. Show() then calls the popup or overlay window which starts its On Show Window Action script. This script will attempt to call a second popup or overlay window; however this will not occur until the first On Show Window Action script completes. A script cannot call itself, even if it is in a different window . The purpose is to prevent infinite loops. However, future versions of InTouch may not behave in this manner.

Test Application

Two versions of an InTouch test application was created to demonstrate how the script engine works.

Note Both test applications are available on the Wonderware BBS (phone 949-727-0726 ).

This application does the following:

  • Successive scripts were defined, starting with the outermost script shell and progressing inward, including paths for the interrupt vectors.
  • Each successive script updates a new tag with a counter. Once each tag is updated, the counter will increment. This defines a specific operation path for the script engine.
  • Different script types are tried in various orders to show how the script engine operates and to predict what the script engine will do so that a flowchart of the script sequence is created.

The following lists the scripts that were written for our InTouch test application.

Window Scripts for “Test Scripts”: Script on Show: g = startupvar; startupvar = startupvar + 1; i = startupvar; startupvar = startupvar + 1; k = startupvar; startupvar = startupvar + 1; l = startupvar; Application Script:

On Application Startup: testvar = 1; startupvar = 1; A = startupvar; startupvar = startupvar + 1;

While application running, every 100msec: IF testvar THEN startupvar = startupvar + 1; b = startupvar; startupvar = startupvar + 1; d = startupvar; startupvar = startupvar + 1; f = startupvar; startupvar = startupvar + 1; testvar = 0; ENDIF;

Condition Script: Condition: b > 0 Comment:

On True: startupvar = startupvar + 1; c = startupvar;

Condition Script: Condition: g > 0

On True: startupvar = startupvar + 1; o = startupvar; Data change script: On Changes to b: startupvar = startupvar + 1; e = startupvar;

Data change script: On Changes to g: startupvar = startupvar + 1; j = startupvar;

Data change script: On Changes to j: startupvar = startupvar + 1; m = startupvar;

On True: startupvar = startupvar + 1; h = startupvar;

Condition Script: Condition: j > 0 Comment:

On True: startupvar = startupvar + 1; n = startupvar;

Condition Script: Condition: p > 0 Comment:

On True: startupvar = startupvar + 1; q = startupvar; Condition Script: Condition: A > 0 Comment:

On True: startupvar = startupvar + 1; s = startupvar; Condition Script: Condition: r > 0 Comment:

On True: startupvar = startupvar + 1; End1 = startupvar;

Condition Script: Condition: n > 0 Comment:

Data change script: On Changes to m: startupvar = startupvar + 1; p = startupvar;

Data change script: On Changes to o: startupvar = startupvar + 1; r = startupvar;

Data change script: On Changes to s: startupvar = startupvar + 1; t = startupvar;

Data change script: On Changes to q: startupvar = startupvar + 1; End2 = startupvar;

The application window shown in Figure 4 was developed based on the scripts shown on the previous page. The values displayed on the window are from ‘startupvar’ which was initialized in the On Startup Application script and is incremented each time a new tag is updated with the new value. These are the script thread values which show the path that the script engine “travels” through all the scripts. The letters preceding the thread values are the actual tagnames which you can reference in the listed scripts. Notice that the scripts demonstrate the script engine’s order of execution that is shown in Table 1.

Figure 4. InTouch “Test Scripts” Application Window

By changing the Condition script which initiates tag ‘o’ to a scanned While True Condition script with a scan time of 1100 milliseconds, an interrupt vector will be generated from a script “holding area” when the timer expires. The result is that the Data Change and Condition scripts in its branch will not start as shown in Figure 5.

Figure 5. Test Application Window with the Modified Condition Script

For our test application, the scan time was set to 1100 milliseconds. This value was chosen because it illustrates the Condition script timer expiring and the halted processes resuming. Notice where the interrupt vector appears in the timed script. If this same application were to run on a different computer, the processes running on each computer might be different enough to cause the interrupt vector to appear in another location. Also, the Tick Interval will not affect where the interrupt vector will appear. (The test application was run using a 100 milliseconds Tick Interval and a 100 milliseconds system tag update time.)

A common question is “When are DDE tags updated?” Based on our test application, in most instances, DDE tags will not be updated until the Tick Interval is reset. But why is this? In order for DDE tags to be updated, or be active , one of the following ten conditions must occur:

  • A DDE topic is set to Advise All Items .
  • A tagname is displayed in an open application window.
  • A tagname is used in a Window script or Action script and the application window that is associated with either script is opened.
  • A tagname is actively being used by a real-time trend.
  • A tagname is used for alarm purposes.
  • A tagname is event logged and Event Logging is enabled.
  • A tagname is being logged in an Historical Log file and Historical Logging is enabled.
  • A tagname is used in a Key, Condition, Data Change, or Application script.
  • A tagname is an Auto Collection tagname in the SPC Program.
  • A tagname is currently accessed by a client application (such as Microsoft Excel) using DDE.

Except for item 1, the tagname in each of the nine remaining items has some link to a script or an application window (which has a link to a script). Since we know when the DDE tag is updated, we can assume that the tag under these conditions will be updated at approximately the Tick Interval. Item 1 is based on the I/O Server, so the update time is equal to the I/O Server’s scan time (which is set in the I/O Server or topic—it has nothing to do with the script engine). When a DDE item is written by a user input, such as pressing the <Enter> key, it will cause an interrupt vector. The interrupt vector makes a request to the server to write the value to the DDE device at the DDE server’s tick time.

Updates from the DDE device will not appear immediately if it is routed through the script engine.

As an example, create the following items in a new application:

  • A DDE User Input tag which is a hot link to an Microsoft Excel spreadsheet;
  • A separate value display using the DDE tag as the expression;
  • A display for a tag value being updated from a DDE tag through an immediate Data Change script (this runs as an instance of the variable database being changed by DDE);
  • A display for a tag value being updated from a DDE tag through an immediate, unscanned Condition script (this runs as an instance of the variable database being changed by DDE).

Then, show the Excel spreadsheet on the monitor screen underneath the InTouch windows. By setting the Tick Interval to 20000 milliseconds, the application shows that as the User Input tag value is changed, the Excel spreadsheet, the DDE tag value, and the rewritten tags are all immediately updated at the conclusion of the interrupt vector and displayed to the screen, regardless of what the Tick Interval value. (This could be a Pushbutton Action script instead of a user input.)

Choosing the appropriate scan time/Tick Interval for these scripts may require tuning in that you may need to try several values until a satisfactory value can be found. The reason for this is that there are many other processes running within InTouch that need to be serviced, in addition to the script engine. Some of these processes are DDE accesses, screen updates, and checking for messages for the WW Logger. Each of these services require some system overhead time, and depending on how complex your application is, the appropriate scan time/Tick Interval will vary for each application. Note that while within the script engine, some external processes may not be updated. Be very careful when you make assumptions about external processes such as updating DDE tagnames . The simplest thing to do is to make your scripting as streamlined as possible and to avoid unnecessary coding or loops which can consume system overhead time.

There are many factors which affect the InTouch script engine. If you are not careful when designing the scripts for an application, your application may result in an undesired behavior. To avoid problems with complex scripts, you should graphically display what you want to accomplish in your scripts. This can be done by flowcharting your application before you start to write your scripts. Also, optimizing your application may require proper nesting of scripts and adjusting the scan times.

Appendix A: Some Scripting Related Questions and Answers

Q how are scripts scheduled for execution in an intouch application.

A All scripts are event driven. Events may be a data change , a condition, a timer, and so on. The order that scripts are executed is different for each application. Thus, there is no guaranteed order of execution. Scripts execute in the order that their associated triggering events have occurred. This means that two events cannot happen at the same time under Windows; every event has a unique time stamp. Currently, the order of execution for scripts is based on event time stamps and nothing else. If a tag is defined as retentive and is modified in a script, then the file I/O is performed when its value is changed. Normally, there are no peeks while the script is executing. Any script that brings up a dialog or selection box will peek to allow the processing of the Windows messages (that is, DDE input is processed, keystrokes are processed, time initiated scripts are processed, and so on). This means that other scripts may be initiated while the dialog or selection box is displayed. In this case, the dialog or selection box does not regain control until the scripts that were initiated are completed.

A Data Change script may act like a subroutine within a script. If the value of a tag, which is linked to a Data Change script, changes while another script is executing, the script that is currently running will pause. The Data Change script will then execute and complete, then the other script will then resume executing. Similarly, if a line in a Data Change script changes the value of a tag that is tied to second Data Change script, the first Data Change script will pause, the second Data Change script will execute, then the first Data Change script will resume executing.

Data Change scripts run only when a tag value is updated. If the tag value comes from DDE, then the Data Change script executes once the DDE value is received. If the tag value is changed by another script, then the Data Change script will immediately run and complete before InTouch will execute the next line in the script. Note that this could cause an infinite loop. To prevent this from happening, do not allow a script to execute if it is still active .

Here are two examples of how to prevent an infinite loop in a script:

Script1 calls Script2 which calls Script3 which attempts to call Script2 . In this case, the last attempt to call Script2 will not execute.

Data Change script on Tag1 which modifies Tag2 . Data Change script on Tag2 . Script1 modifies Tag1 .

Q What is the sequence and timing for executing commands within a script?

A Each command in a script is executed in the order that it is found in the script file. Each command is completed before it moves to the next command, except for functions that require a DONE Tag such as FileMove() , FileCopy() or PrintWindow() .

The following lists the order of precedence, in descending order, for evaluation of script operators. Operators that are listed in the same line have equal precedence.

Q What are the effects of blank and comment characters in a script?

A Blank and comment characters are ignored. When the left brace ({) is encountered, the script engine continues ignore the characters until a right brace (}) is encountered. This may be used in any InTouch script or expression box. Its effect on the timing of the script is negligible. Wonderware Technical Support recommends that you document your scripts and expressions with comment characters.

Q How do scripts interact? What is predictable and what is not?

A If one script is currently running and a second script is triggered, then the first script is halted and the second script executes to completion. Any tagnames that are changed by the second script will be seen by the first script. If a tag value is changed, then the message is changed immediately, but it will not be processed by the receiving program (that is, a I/O Server) until at least at the end of the script. When the DDE server gets the processor, it will do the write on its next message for that particular topic.

Q How can I use a script as a subroutine?

A Data Change scripts can be used as subroutines for other scripts. That is, if the Data Change script is started from another script, InTouch will branch out and run the Data Change script as a subroutine.

For example, say you have a Data Change script based on a tag called Trigger and the script uses “value = value + 10;”. Then, you can create a Pushbutton or a Condition script with these lines:

trigger = not trigger; trigger = not trigger; trigger = not trigger; trigger = not trigger;

When this script runs once, the Data Change script on Trigger runs each time “trigger = not trigger” was interpreted while it was still processing the first script. This technique allows you to set up subroutines that can be triggered from inside of other scripts. This can be extremely powerful!

However, a change in a tag value should not be used to call a previously triggered script (that is, if Script1 calls Script2 which calls Script3 which attempts to call Script1 , the final call will not be executed). That is, tagnames that are modified in a Condition script or a Data Change script should not be used as the tagname for a Data Change script or in the expression of a Condition script.

A Data Change script that executes based on the value of tag “A”, contains the line “B=B+1.” Tagname “B” should not be used as the tagname for a Data Change script or be part of the expression for a Condition script.

You can also “nest” your scripts (that is, have a script call another script which calls another script, and so on). But, the maximum number of nested levels for script execution depends on the size of the window stack which is represented by the Stack parameter value in your SYSTEM.INI file. (During installation, InTouch ensures that the Stack parameter is set to a value that is adequate for most scripting situations.)

Q Can I modify the value of a tag that is linked to a Data Change script which is within the body of the same script? What about modifying tag values that are linked to a Condition script?

A If the tagname is linked to a Data Change script (that is, it is listed in the Tagname field), and the tag’s value is changed within the body of that same script, then it will modify the value once. For example, if the tagname MYTAG is linked to a Data Change script that contains the line “MYTAG = MYTAG + 1”, then the script will increase the value of MYTAG by one and exit. The Data Change script will not execute again. This feature prevents infinite loops. If the MYTAG value is redefined several times in the Data Change script, then each redefinition will take place, but the Data Change script will not execute again.

Warning Do not write any scripting which relies on this behavior. Future versions of InTouch may not operate following this logic and it will expose your application to upgrading difficulties and/or incompatibilities.

If you modify the value of a tag that is linked to a Condition script, then within the body of that same script, the results may not be what you expect. The following is an example of what happens when you do this.

Here is an example Condition Script:

Condition: ( X == 25 ) ON TRUE body: PrintWindow(“Main Window”); Blob = TAN(MyAngle); X=0; AppActivate(“NotePad”); Show(“Blaster Control”); TagLevel = NewValue * SIN( Blob ); ON FALSE body: SendKeys(“Main Window Printed”); Blob = 0;

You would think that execution would proceed as follows:

PrintWindow(“Main Window”); Blob = TAN(MyAngle); X=0; AppActivate(“NotePad”); Show(“Blaster Control”); TagLevel = NewValue * SIN( Blob ); SendKeys(“Main Window Printed”); Blob = 0;

That would make sense; the ON-TRUE script executes first, then the ON-FALSE. But what really happens is this:

PrintWindow(“Main Window”); Blob = TAN(MyAngle); X=0; [ Pause the ON-TRUE ] [ Launch ON-FALSE ] SendKeys(“Main Window Printed”); Blob = 0; [ Resume ON-TRUE ] AppActivate(“NotePad”); Show(“Blaster Control”); TagLevel = NewValue * SIN( Blob );

Now the value of Blob has been reset to zero (at the wrong time), and more importantly, the SendKeys has failed to make it to Notepad, since it was not active yet.

Q If a tag is defined as retentive and its value changes in a script, then is the file I/O performed immediately after the value changes?

A No, not until the script has finished executing.

Q Is it true that if a tag value changes within a script, then any Data Change scripts will immediately execute? What about Condition scripts set to On True or On False—will these types of Condition scripts immediately execute that are affected by the new tag value?

A Yes for both questions. Be aware that this feature may change in a future InTouch version. In general, you should not rely on the Data Change scripts or the Condition scripts executing immediately after the tag value changes. Instead, you should design your system to avoid this type of condition from happening. This way, your application will not be adversely affected when you upgrade to a future version of InTouch.

Q Does a script need to run to completion before it can give up the CPU? At what point in the script can I do a PeekMessage to give other applications a chance to run? Also, if a script triggers or executes other scripts, when does the CPU get released?

A The script engine will capture the CPU during script execution. At no time during the execution of a script will the CPU be released. PeekMessage( ), which is an Windows API call, is not performed while the script executes.

Q Is there an upper limit to the nesting level for executing scripts? How many scripts can be chained together to execute in a series through a Data Change script?

A Wonderware Technical Support is not aware of an upper limit on how many scripts you can nest or how many scripts you can chain together. Be aware that the practice of purposely chaining scripts together is not a good idea because there may be incompatibility issues with future versions of InTouch.

Q If a tag value changes, then is the new value immediately sent to the I/O Server? If not, when? When does the I/O Server perform the write? Is a I/O Server inactive while the script executes?

A No, the new tag value is not immediately sent to the I/O Server. Instead, the value is sent after the script completes. And, yes, the I/O Server is temporarily inactive while a script executes.

Q When are the system tags for date and time updated? (that is, $Month, $Day, $Year, $Time and so on) I thought they were updated based on the value of the Update Time Variables setting under /Special/Configure/View Generic. I wrote a script to change the time and date, and $Time changed, but $Date did not update until WindowViewer was restarted.

A If you change the system time or date on your computer, then be sure to send a WM_TIMECHANGE message to all Windows applications that are currently running, including your InTouch applications. Windows applications rarely “poll” for the current time.

Q I want to print a file from my InTouch application. How can I do this?

A You can use the FileCopy()function, as in this example:

status = FileCopy(“c:\autoexec.bat”,”lpt2″, Monitor.Name);

This prints the AUTOEXEC.BAT file on printer LPT2 and uses the Integer tag Monitor as the DoneTag. (Note: You can also use the FilePrint() function that is part of the Technical Support script function library which is available on the  Knowledge Base CD.)

Q How do I create a timer in InTouch?

A Use a Condition script that references a Memory Discrete tag named Flag. Then, create an On True Condition script that references a tag named Counter that is set to zero (that is, initialize the counter). Set the time, While True Every xxx msec, to the desired frequency. The While True Condition script should increment the Counter tag every time the script runs. When Counter is greater than a preset value, set a Memory Discrete tag called Trigger to one (1).

Counter = Counter + 1;

IF Counter >= 5 THEN Trigger = 1; ENDIF;

Q How do I toggle a Discrete tag from a script?

A Try the following:

Discrete = 1 – Discrete;

Discrete = NOT( Discrete );

Q I am using the On Shutdown Application script to kill the applications ‘App1’ and ‘App2’ when my InTouch application exits (by choosing Exit from the File menu). Here is my On Shutdown Application script:

ActivateApp “App1”; SendKeys( “%(f)” ); SendKeys( “x” ); ActivateApp “App2”; SendKeys( “%({F4})” );

However, when I exit my application, ‘App1’ and ‘App2’ are still running. I copied this script into a Pushbutton Action script and the two applications were killed. Why does my On Shutdown Application script not work?

A Because WindowViewer captures the CPU during shutdown, you cannot run a script which would cause another application to get access to the CPU. Thus, the ActivateApp( ) function will not work in an On Shutdown Application script.

Q Then, why is there no KillApp( ) script function?

A There is a script function called WWControl( ). WWControl( ) can be used to terminate an application. If you use it in your Application Action script, then it will kill your applications during shutdown. For example, insert this line into an On Shutdown Application script:

WWControl( “Notepad – (Untitled)”, “CLOSE”);

Run Notepad without loading any files and then start up the application under WindowViewer. Exit WindowViewer and the Notepad application should disappear.

Appendix B: Indepth Review of Complex Script Functions

The InTouch script editor supports two “styles” of scripts: Simple and Complex . Simple scripts allow you to do assignments, comparisons, simple math functions, and so on. Complex scripts allow you to perform logical IF-THEN-ELSE type statements and to process loops using the FOR-NEXT script structure. In addition, InTouch also supports the use of built-in complex script functions, such as StartApp().

Functions with the argument ApplicationName will start the Windows application identified in the ApplicationName argument. These type of functions may be used in both simple and complex scripts.

Note that each IF statement requires an ENDIF statement. Also be aware that an ELSE statement is not required if your script does not need it.

The first NEXT closes the inner FOR loop and the last NEXT closes the outer FOR loop. Likewise, in nested IF statements, the ENDIF statements automatically apply to the nearest prior IF statement.

The following is an example of an IF-THEN-ELSE script:

IF React_temp > 200 THEN React_temp_sp = 150 PRValve = 1 PlaySound (“c:\fire.wav”,1); ELSE PRValve =0; PlaySound (“c:\All_Ok.wav”, 1); ENDIF;

In this example, the script checks if React_temp is greater than 200. If so, then Reactor_temp_sp is assigned the value of 150, PRValve is turned on and the FIRE.WAV file is played. If React_temp is equal to or less than 200, then the PRValve is turned off and the ALL_OK.WAV file is played. Note the use of the PlaySound(path_text,number) in this script.

Here is an example of a FOR-NEXT loop script. This loop performs a simple iterative mathematical calculation. When executed, Product equals the value of NumberToRaise raised to the power of 10 (that is, Product=NumberToRaise10).

Product = 1; NumberToRaise = 4; FOR Index = 1 TO 10 Product = Product * NumberToRaise; NEXT;

Once the above script has completed processing, the value of the Product will be 1,048,576.

Here are some more examples of various complex scripts:

IF-THEN statement with no ELSE clause:

IF a <> 0 THEN a = a + 100; ENDIF;

IF-THEN-ELSE statement with one ELSE clause:

IF temp > 500 THEN Disc = 1; Real = 43.7; ELSE Disc = 0; Real = 93.4; ENDIF;

IF-THEN-ELSE statement with one ELSE IF clause and no ELSE clause:

IF temp > 500 THEN Disc = Disc * 10; ELSE IF temp > 250 THEN x = y / z; a = abc + def; ENDIF; ENDIF;

IF-THEN-ELSE statement with one ELSE IF clause and one ELSE clause:

IF temp > 500 THEN Disc = Disc – 10; ELSE IF temp < 250 THEN Disc = Disc + 10; ELSE Disc = Disc + 50; Real = 100; ENDIF; ENDIF;

IF-THEN-ELSE statement with multiple ELSE IF clauses and one ELSE clause:

IF temp > 100 THEN temphihi = 1 Disc = 50; ELSE IF temp > 80 THEN temphi = 1; ELSE IF temp < 30 THEN templo = 1; ELSE IF temp < 10 THEN templolo = 1; ELSE tempok = l; ENDIF; ENDIF; ENDIF; ENDIF;

IF-THEN-ELSE statement that tests for Condition 1 or Condition 2:

IF (pump1 < 50.0) OR (pump2 < 50.0) THEN alarm-1 = 1; ELSE alarm-1 = 0; ENDIF

IF-THEN-ELSE statement that tests for Condition 1 and Condition 2:

IF (pump1 < 50.0) AND (pump2 < 50.0) THEN alarm-2 = 1; ELSE alarm-2 = 0; ENDIF;

IF-THEN-ELSE statement that tests for equivalency:

IF a > 50 THEN IF b == 100 THEN c = 0; ENDIF; ENDIF;

Control structures can be placed inside other control structures (such as an IF-THEN block within a FOR-NEXT loop). A control structure inside another control structure is known as nesting. Example:

FOR TagX = 1 TO 5 FOR TagY = 1 TO 10 …statements… IF (condition) THEN [EXIT FOR;] ENDIF; …statements… NEXT; NEXT;

You might also like:

Itsyslog script function, wmkgetdscalarmpriority script function, how to avoid script timeout warnings in intouch, leave a reply cancel reply.

Your email address will not be published. Required fields are marked *

Magic Captcha 59 + = 65

expecting assignment intouch

  • Title: The ArchestrAnaut » Wonderware blog
  • Channel Number: 14484896
  • Language: eng
  • Registered On: May 29, 2013, 2:22 am
  • Number of Articles: 10
  • Latest Snapshot: September 19, 2014, 4:52 pm
  • RSS URL: http://www.avidsolutionsinc.com/blog/archestranaut/tag/wonderware-blog/feed
  • Publisher: http://www.avidsolutionsinc.com/blog/archestranaut
  • Description: Covering everything in the Galaxy
  • Catalog: //morntime27.rssing.com/catalog.php?indx=14484896

Auto Assigning IO

One of the things we do a lot of is automatically assigning IO to a corresponding IAS object or a PLC data structure.  We’ve tried several different auto-assignment schemes.  We had two main requirements:  be scalable and be reliable.

Version 1.0:  The base template contained a single bit that is the trigger for all IO assignment scripts in all of the derived templates.  The base template also had an OnScan script that set the bit to true.  The bit could also be toggled via ObjectViewer for testing or troubleshooting purposes.  Each derived template that needs to do auto assignment has an OnTrue script for the trigger bit.  Unfortunately, the OnTrue script bit us on some massive deploys.  Most of the objects would connect up but some did not.  This all ties back to the way OnTrue scripts work .

Version 1.1:  We changed all of the OnTrue scripts to WhileTrue.  We also changed the expression to be the trigger bit is true and a specific IO target was equal to “—“ (the prescribed default).  The specfic IO target used was the last one written to in the body of the script.  This was done to ensure that the script finished executing.

This method is now 100% reliable, but it does have some downsides.  The original benefit of having a bit that can be toggled from ObjectViewer is gone.  The IO target has already been set once, which prevents the script from executing again.  This same thing is annoying in development when you make an error & have the wrong target.  A deploy changes doesn’t reset the IO target to “—“.  If you do want to reset the IO target, you’ll need to set the object off scan first.

Version 2.0:  We came up with a way to keep the 100% reliability but retain the original benefit of testing / troubleshooting via ObjectViewer.  The first step is to migrate from a bit to an integer.  The integer can be set to 0 (all bits off) or –1 (all bits on) in the original OnScan script.  All auto assignment scripts in the derived templates would be assigned a unique bit out of the 32 available.  Each script would be a WhileTrue script where the expression is expecting that bit to be off (OnScan set the integer to 0) or on (OnScan set the integer to -1).  The script would then do the assignment and flip the bit at the end to prevent the re-execution of script.

I created a short example using the “set to 0” method.  There are 3 template levels 0, 1, & 2.


Template level 0 contains the OnScan script and the first level of auto-assignment.  In our real world usage, the base template contains only the auto-assignment and other basic functionality and no IO at all.  But this is a 10 minute example & it’s free.


At the second and third levels, you have:


There isn’t a whole lot of difference between the two methods (0 vs –1).  I prefer the “set to 0” method for a couple reasons.  First, the default of the data type is 0 and the default of the variable is likely to be 0 (especially for the time-constrained, careless programmer).  If something does go wrong and the value gets reset to the data type default, at least you’re covered (execution is still 100% reliable).  I’ve never ran across this and can’t come up with a scenario how it would happen.  However, it’s running on software on a PC, not a PLC.  There are a lot more layers in PC programming to introduce bugs or dependencies that can make your code break.  Second, when you look at the result after everything has finished executing (what should be steady-state), the result is a likely going to be a small, positive number (see below).  It’s easier for me to see that a value of 7 means that levels 0, 1, & 2 have all executed instead of –8.


WriteStatus Function in QuickScript Library

I’m currently exploring ways of verifying a write has been completed or gets stuck.  I ran across the WriteStatus function that comes with the QuickScript function library.  I was trying to use it, but it always returned MxStatusOk even for items with bad quality.

I did a little digging with Red Gate’s DotNET Reflector.  I decompiled the WriteStatus function in QuickScriptLibrary.dll.  The function is this:

[ FunctionCategory ( "Miscellaneous" )] public static MxStatus WriteStatus ( ExternalReference Attribute) {     return MxStatus . MxStatusOk ; }

Not sure why it’s hardcoded to always return OK.  The IOM guys that are reading may be able to shed some light on this (backwards compatibility?).

For what it’s worth, I tried pulling Me.UDAName.WriteStatus (for an InOut extended UDA) and that always pulls the same value as an integer (-2147483648 or 0×80000000).  I haven’t figured that one out yet, but it’s still not very useful.

What I did find that is the following:

Dim StatusObj

as Object ; ’ Retrieve the Status object StatusObj = Me.InOutExtendedUDA.WriteStatus ;

This yields the same text that you see in object viewer (i.e. “”, “Operation pending”, “Configuration error: Invalid attribute ID”, etc.).  Now that’s useful!

Interfacing to a SQL Database

In our work we do a lot of interfacing with SQL databases (usually MS SQL Server).  Being able to interface with SQL from IAS is a big weapon in one’s arsenal.  You can retrieve data from a table or view, execute stored procedures and functions, and virtually anything else you can dream of doing with T-SQL.

We’ve wrapped most of the code to do the SQL DB interfacing into a DLL for simplicity and error trapping.  However, this post is intended to give a basic example on executing a simple query from an IAS script using the .NET objects.

With or without the DLL wrapping up the SQL code, there are still several possible failures that we must account for (timeout, untrapped exceptions, etc).  Handling those in IAS will require some extra structure to process and take appropriate actions.  The main benefit to the DLL method is that it turns 50 lines of code into 2 and modularizes it (reduces testing requirements).  All of the error trapping structure is outside the scope of this post (sorry).

In the example below, the basic concept is to do the following:

  • Create a SQL connection (ideally a trusted connection)
  • Open the connection
  • Create a SQL command using a T-SQL query string
  • Execute the command
  • Close the connection
  • Print the data to the SMC

Included are two different options for “executing” the SQL command.  Option 1 is to get a single (scalar) value.  It returns the data in row 1, column 1 of the resulting data.  Option 2 is to return the full data set as an object.  I have changed the query, DB name, etc from the system I tested this on, but it’ should not be hard to test it on another DB.

Connection strings are always a pain.  Here is a great website for building your own SQL (and other) connection strings.

The script config is:


The body of the script is:

Dim QueryStr as String ; Dim SqlConn as System.Data.SqlClient.SqlConnection ; Dim SqlCmd as System.Data.SqlClient.SqlCommand ; Dim SqlDA as System.Data.SqlClient.SqlDataAdapter ; Dim DS as System.Data.DataSet ; Dim Tables as System.Data.DataTableCollection ; Dim Table as System.Data.DataTable ; Dim Row as System.Data.DataRow ; Dim Column as System.Data.DataColumn ; Dim ScalarValue as Object ; Dim i as Integer ; Dim j as Integer ; Dim TempStr as String ;

‘ Create the query to execute QueryStr = "SELECT " +     "[ColumnA], " +     "[ColumnB], " +     "[ColumnC] " +     "FROM [DBName].[dbo].[TableName] " +     "WHERE [ColumnA] > 50 " +     "ORDER BY [ColumnA] ASC" ;

‘ Create the SQL connection object using the connection string. SqlConn = New System.Data.SqlClient.SqlConnection( Me. ConnectionString );

‘ Open the SQL Connection SqlConn.Open();

‘ Create a SQL command object. SqlCmd = New System.Data.SqlClient.SqlCommand( QueryStr , SqlConn );

‘ Executes the query, and returns the first column of the first row in the result set returned by the query.  Additional columns or rows are ignored. ScalarValue = SqlCmd.ExecuteScalar();

‘ Create a SQL data adapter object to retrieve table data. SqlDA = New System.Data.SqlClient.SqlDataAdapter( SqlCmd );

‘ Create the DataSet object. DS = New System.Data.DataSet();

‘ Use the SQL data adapter to fill the data set with the query result data. SqlDA.Fill( DS ); ‘ Close the SQL Connection SqlConn.Close();

‘ Log message about scalar value. LogMessage( "Scalar Data = " + ScalarValue.ToString());

‘ Cache a reference to the table collection Tables = DS.Tables ;

font color="#008000" size="2" face="Courier New">‘ Reset the table counter i = 0;

‘ Iterate through tables, rows & columns to print all the data For Each Table In Tables     ‘ Log message about table     LogMessage( "Table " + i );         ‘ Reset the row counter     j = 0;         ‘ For each row, print the values of each column.     For Each Row In Table.Rows         ‘ Set the row message header         TempStr = "Row " + j + ": " ;                 ‘ Iterate columns         For Each Column In Table.Columns             TempStr = TempStr +                 StringChar(34) +                 Row( Column ).ToString() +                 StringChar(34) + "," ;         Next ;                 ‘ Trim off final comma         TempStr = StringLeft( TempStr ,             StringLen( TempStr ) – 1);                 ‘ Log row message         LogMessage( TempStr );                 ‘ Increment row counter         j = j + 1;     Next ;       ;   ‘ Increment table counter     i = i + 1; Next ;

A How-To Link to with BindTo

We’ve run across several scenarios where we want to link to an indeterminate or variable number of items during a script’s execution to do some kind of work with them.  Among other things, we have used this for iterating through a pair of parameter lists in a formula object to perform a compare between the field IO & the loaded formula.  Using IO extensions are too slow to update & read back within a single scan, but you can use the BindTo command with the Indirect type to do what you need!  The BindTo-Indirect combination works like a  pointer for IAS (this one , not this one ).

FYI, there is a pretty significant limitation.  It only works for communication to points within the same AppEngine as the script it’s being called from.  Though I think it’s technically permissible in an ArchestrA graphic, it’s not of much use there.  The graphic runs on a ViewEngine, so BindTo should only be access data on that ViewEngine.

To use it, you first need a  local script variable of type Indirect.  To make it connect up to some other memory location, you’ll need to use the BindTo command which has the following syntax:  IndirectVariableName.BindTo(“Valid IAS Reference”).

The example below is a quick snippet on binding to to different locations & ensuring the bind worked properly.  Our experience is that binding is usually pretty quick when it is going to work.  There are some other caveats about connecting to points with non-good quality, but that’s for another day.

Dim Working as Indirect ; Dim Target as Indirect ; Dim i as Integer ; Dim Count as Integer ; Dim BindError as Boolean ;

‘ Iterate though parameters For i = 1 to Count     ‘ Set the default result for a binding error     BindError = False ;         ‘ Bind to "working value" parameter     Working.BindTo( Me.FormulaTagname + ".Working." +     Me.ParameterNames [i] + ".SP" );          ‘ W ait for the quality to be good or a timeout count     ‘ Typicially connects in under 10 counts     For Timer = 1 to 5000         If IsGood( Working ) then             Exit For ;         EndIf ;     Next ;         ‘ Set the error flag if the binding took too long     If Timer > 5000 Then         BindError = True ;     EndIf ;         ‘ Bind to "target value" parameter     Target.BindTo( Me.FormulaTagname + ".Target." + Me.ParameterNames [i] + ".SP" );         ‘ Wait for the quality to be good or a timeout count     ‘ Typicially connects in under 10 counts     For Timer = 1 to 5000         If IsGood( Target ) then             Exit For ;         EndIf ;     Next ;         ‘ Set the error flag if the binding took too long     If Timer > 5000 Then         BindError = True ;     EndIf ;         ‘ If the binding was successful, do some work     If Me.VerifyQuality Then         ‘ Do Work…     EndIf ; Next ;

ArchestA Graphics Notes

Just ran a quick test on ArchestrA graphics to verify when the OnShow & OnHide scripts execute.  I was also testing if AA graphic objects’ visibility properties are updated when the InTouch visibility was updated.  Nothing Earth-shattering here, but just a quick FYI.

I created a simple AA graphic with a button to toggle visibility on a rectangle.  I dropped that down in InTouch with another button to toggle the visibility on the whole AA graphic.  The AA graphic also has simple LogMessage commands in the OnShow & OnHide scripts.  I also had a regular script that executes once.

What I found was that whenever an InTouch screen loads, the AA graphic executes the OnShow script then the regular script.  When the screen closes, the OnHide script executes.  Toggling the InTouch visibility on the AA graphic did not trigger either the OnShow or OnHide scripts.

Toggling the InTouch visibility on the AA graphic also does not update the visible parameter on the objects within the graphic.  That acts as expected.  I was hoping there was a built-in easy way of detecting when the whole AA graphic changes visibility from within itself.  The purpose would have been to refresh data (i.e. on a data grid) whenever swapping between some of our tabbed graphics.  We currently expose a parameter as public to force the refresh from the containing AA graphic.

Also, if I remember right, there are issues with remote IO reference connections making that OnShow script unreliable.  I believe that has either already been corrected in the latest release or might be on the list of issues to fix in the next release.  If someone would like to post a comment on the details of the issue and/or the fix, I’d be grateful.

Watching a Horde of Objects

Every now & then I run across a scenarios where I have to build a watch window for hundreds of objects.  If I built watch windows like they teach you in the training class (click & drag in ObjectViewer), I’d still be building them.  There’s a far easier way to build large watch windows.

The whole premise is built around the fact that the “saved watch window” file format is easy to read & parse (thank you Invensys!).  The first step is to manually build the first watch window in ObjectViewer & save it (see below).


You end up with a text file that looks like the text below.  I cleaned it up visually by adding hard returns between each section.  FYI, it will open back up in ObjectViewer just fine in the cleaned up format.

<VisualLMXTestTool> <Watch Tab="Watch List 1"> <ReferenceString>ObjectName.PV</ReferenceString> <ReferenceString>ObjectName.SP</ReferenceString> <ReferenceString>ObjectName.OUT</ReferenceString> <ReferenceString>ObjectName.Mode</ReferenceString> </Watch> </VisualLMXTestTool>

Next, I get a list of the objects I need to have in the watch window, usually from a Galaxy Dump CSV file.  Using a couple tricks in Excel, you can very easily replicate the format above to several hundred objects split across however many tabs you want.  In the example below, the tabs are broken out by area.

<VisualLMXTestTool> <Watch Tab="Area A"> <ReferenceString>ObjectName1.PV</ReferenceString> <ReferenceString>ObjectName1.SP</ReferenceString> <ReferenceString>ObjectName1.OUT</ReferenceString> <ReferenceString>ObjectName1.Mode</ReferenceString> <ReferenceString>ObjectName2.PV</ReferenceString> <ReferenceString>ObjectName2.SP</ReferenceString> <ReferenceString>ObjectName2.OUT</ReferenceString> <ReferenceString>ObjectName2.Mode</ReferenceString> <ReferenceString>ObjectName3.PV</ReferenceString> <ReferenceString>ObjectName3.SP</ReferenceString> <ReferenceString>ObjectName3.OUT</ReferenceString> <ReferenceString>ObjectName3.Mode</ReferenceString> </Watch> <Watch Tab="Area B"> <ReferenceString>ObjectName4.PV</ReferenceString> <ReferenceString>ObjectName4.SP</ReferenceString> <ReferenceString>ObjectName4.OUT</ReferenceString> <ReferenceString>ObjectName4.Mode</ReferenceString> <ReferenceString>ObjectName5.PV</ReferenceString> <ReferenceString>ObjectName5.SP</ReferenceString> <ReferenceString>ObjectName5.OUT</ReferenceString> <ReferenceString>ObjectName5.Mode</ReferenceString> <ReferenceString>ObjectName6.PV</ReferenceString> <ReferenceString>ObjectName6.SP</ReferenceString> <ReferenceString>ObjectName6.OUT</ReferenceString> <ReferenceString>ObjectName6.Mode</ReferenceString> </Watch> <Watch Tab="Area C"> <ReferenceString>ObjectName7.PV</ReferenceString> <ReferenceString>ObjectName7.SP</ReferenceString> <ReferenceString>ObjectName7.OUT</ReferenceString> <ReferenceString>ObjectName7.Mode</ReferenceString> <ReferenceString>ObjectName8.PV</ReferenceString> <ReferenceString>ObjectName8.SP</ReferenceString> <ReferenceString>ObjectName8.OUT</ReferenceString> <ReferenceString>ObjectName8.Mode</ReferenceString> </Watch> </VisualLMXTestTool>

You ran a W.H.A.T.?!?

We ran into a bootstrap issue on a live system this week.  This customer can’t lose data.  On the plus side, it’s a redundant setup & everything is running fine on one machine.  The problem is that we need to wipe the platform that isn’t running the objects (use Platform Killer).

A word of warning:  Some of the methods I tried are probably not officially supported by WW Tech Support, so we assume no liability if you try it on a live system & something goes wrong!  You try it at your own risk.

I had a theory on how to fix the problem, but I needed to run a Wild Hairy-A–ed Test (a W.H.A.T.).  I created an entire system on one of our ESX servers using VMWare Converter to do a P2V (physical to virtual) conversion.

The IAS setup looked like the picture below with APP_001 as the “screwed up” platform & everything running on APP_002.


The Test_001/002 objects historize a PV value that continuously increments from 0 to 100 (adds 1 every scan & resets at 100).

1) Undeployed Test_AppEngine_001 (Primary) & Test_AppEngine_002 (Backup) with the following options: In our scenario, it is critical to NOT undeploy the Test_001 object.  Undeploying it will remove it from the backup & the running system.  To do this, it is critical to not do a cascade undeploy or the redundant partner.  Note: This leaves the Test_001 object marked as deployed! 2) I tried to undeploy the platform using the Cascade Undeploy = False option, but it was locked.  I tried selecting it & another undeployed object to trick it, but it whines about the Test_001 object not being selected to be undeployed. 3) I ran Platform Killer on APP_001.  Here comes the unsanctioned trick… 4) I opened up SQL Server Management Studio & opened the gobject table for the galaxy.  I selected the row with APP_001 & reset the data in the deployed_package_id & last_deployed_package_id fields to 0 (from a large integer). 5) I closed the IDE & reopened it to force a graphics refresh.  Note: It will update on its own, but the timing doesn’t seem to be predictable, so I tend to force it. 6) Redeployed APP_001, Test_AppEngine_001 (Primary), & Test_AppEngine_002 (Backup) with the following options (Cascade Undeploy = False & Include Redundant Partner = False). 7) I failed Test_AppEngine_001 back onto APP_001.
1) I ran Platform Killer on APP_001. 2) Attempted to undeploy Test_AppEngine_001 (Primary) & Test_AppEngine_002 (Backup) with the following options: Note: Unlike in test 1, this leaves the Test_001 object marked as undeployed though it is actually still deployed & running on APP_002! Also unlike in test 1, this method detected that APP_001 was already missing & marked the platform as undeployed.  This kept me from needing to do the database trick. 3) Redeployed APP_001, Test_AppEngine_001 (Primary), & Test_AppEngine_002 (Backup) with the following options (Cascade Undeploy = False & Include Redundant Partner = False). 4) I failed Test_AppEngine_001 back onto APP_001.

In both tests, I watched the PV trend live during the whole process.  The only blips I saw were those I expected to see during the failover process.

Harder Than a Needle in a Haystack

I spent last Friday & Saturday writing some SQL code for client to make finding historical data “outages” quicker and easier.  They wanted to know where all of the time spans where the quality wasn’t good.  I was thinking it was a little like a needle in a haystack problem, but it’s not.  It’s harder than that.  It’s more like measuring every piece of hay in the haystack.

Here’s the short version of how the code works.  Before you say it sounds really inefficient, there’s a trick below that helped speed things up.  The SQL code pulls back raw historical data and inspects it for when the data quality changes.  For every time span where the data quality is the same, I would record an entry in a database with the tag name in the historian, the data quality, the start time of the time span, and the end time of the time span.

Doesn’t sound too bad until you look at the size of the pile of data.  I had to run this on a year & a half of data across 4000 tags.

The trick:  I used ActiveFactory Query to play around with several ways of pulling the raw data.  The best way I found was to use the delta retrieval mode with a 100% value deadband.  What that does is allow the InSQL backend that is processing the data in the files to only return results when the value changes 100% of its range.  The trick is that it always returns a values when the quality changes (Technically, its when the OPC quality changes, not the quality detail).  This dramatically speeds up the retrieval of analog tags.  Doesn’t help a whole lot on boolean tags, but 80-90% of the tags I had to process were analogs.  FYI, the reason it doesn’t help on boolean is a change from 0 to 1 (or 1 to 0) is 100% of the range.  It can and does happen with analog tags too, but it’s a far rarer event.

To catch all of the cases where value changes came through on the raw data retrieval, there is some code to process all of the raw rows to cull out the non-quality changes.

It still took about two days of execution time to sift through all of that data.  My rough estimate is that there are somewhere around 50 billion raw data points.  It only takes a few minutes to sort through a single day’s data and cache it off.  I would love to hear if anybody has any other tricks for doing this kind of mining expedition.

Bug in Alarm DB Purge/Archive Utility

I found a “feature” in the Alarm DB Purge/Archive utility last week.  The password entry box will only accept 9 characters.  So, if the password for the account is longer than that, you’re out of luck.


Notes on Quality

I have a couple notes on quality to share.  Not code quality or manufacturing quality, but OPC quality.  For a quick read & good background on how the OPC quality integer is constructed, read this .  I needed a couple SQL functions that mirror the IAS quality checking functions.  FYI, for those that don’t know, there are 5 of these functions:  IsGood, IsBad, IsUncertain, IsInitializing, & IsUsable.

Here’s a somewhat abbreviated run down of how the OPC quality number is constructed.  An OPC quality is made up of 4 chucks: extended status (vendor specific; 1 byte), major status (good, bad, or uncertain; 2 bits), minor status (based on the major status; 4 bits), and limit status (is it clamped or not; 2 bits).  For each major status, there are 16 available minor statuses possible (0-15) though only the first 8 are defined.

I ran a quick test to reverse engineer the IAS functions (see script at bottom of blog).  What I found was the following:

To summarize the table, it looks like they appropriated part of the bad quality range (major = 0) for initializing which makes sense.  Though you could argue that there was already a status defined for this scenario (Not Connected:  Major = 0, Minor = 2).  Major status 2 isn’t used anywhere, so it got lumped into Bad, which also makes sense.  The minor statuses 0-7 for Good (Major = 3) and Uncertain (Major = 1) work as expected, but 8-15 got lumped into Bad.  I know they’re not defined, but they’re still part of the Good/Uncertain major status.  Glad I ran the test.  Maybe this is different in later versions (the tested version is 3.0 SP2).  The final observation is that IsUsable checks the quality for Good or Uncertain as well as checking the value is also a valid value (i.e. not NaN).

The test script: (The one interesting note here is that you can write directly to the UDA.Quality attribute, which could be useful if you’re doing something fancy surrounding the data’s quality.)

Dim i as Integer ; ‘ Log header row LogMessage( "Value,Quality,IsGood,IsBad,IsUncertain,IsInitializing,IsUsable" ); ‘ Iterate through all of the possible OPC qualities For i = 0 to 255     ‘ Force the PV quality     Me.PV.Quality = i ;         ‘ Log data row for the quality     LogMessage(         Me.PV.Value + "," +         Me.PV.Quality + "," +         IsGood( Me.PV ) + "," +         IsBad( Me.PV ) + "," +           IsUncertain( Me.PV ) + "," +         IsInitializing( Me.PV ) + "," +         IsUsable( Me.PV )); Next ; ‘ Log message for end of test LogMessage( "——————-" );
  • //mantisbt21.rssing.com/chan-11737219/index-page1.html
  • //wilhide36.rssing.com/chan-57004361/index-page1.html
  • //isportsweb2112.rssing.com/chan-46235943/article63.html
  • //bitten605.rssing.com/chan-22866254/index-page1.html
  • //heather2787.rssing.com/chan-22866504/index-latest.php
  • //saphir82.rssing.com/chan-74076879/index-page1.html
  • //comentarios20732.rssing.com/chan-46235978/index-page1.html
  • //wapner18.rssing.com/chan-11737788/article4.html
  • //bottlenest73.rssing.com/chan-7871850/index-page1.html
  • //ridgetim4.rssing.com/chan-57003541/article2.html
  • //atmosphere798.rssing.com/chan-57003974/index-latest.php
  • //asylant9.rssing.com/chan-57003498/index-page1.html
  • //migdale57.rssing.com/chan-22865394/index-latest.php
  • //bovarist73.rssing.com/chan-7871988/index-page1.html
  • //allen517.rssing.com/chan-22865304/index-latest.php
  • //sisterhood676.rssing.com/chan-22866074/index-latest.php
  • //tommy760.rssing.com/chan-11736963/index-latest.php
  • //thinning85.rssing.com/chan-22866339/article4.html
  • //motherhood2838.rssing.com/chan-11737344/article10.html
  • //gives1227.rssing.com/chan-22865574/index-latest.php

Latest Images

Javier’s closing its Newport Beach restaurant for a remodel

Javier’s closing its Newport Beach restaurant for a remodel

Polyfill Pillow Inserts - Perfect Size to complete all Atomic Livin Home...

Polyfill Pillow Inserts - Perfect Size to complete all Atomic Livin Home...

Shadow the Hedgehog Comb by SonicTheCosplayer

Shadow the Hedgehog Comb by SonicTheCosplayer

New Hymn Graphic by Norma Boeckler - Behold the Lamb of God

New Hymn Graphic by Norma Boeckler - Behold the Lamb of God

Matt Rife Jawline Surgery Controversy

Matt Rife Jawline Surgery Controversy

Modulation of 3 digital ports of the NI DAQ board

Modulation of 3 digital ports of the NI DAQ board

Mennonite families set sights on farming in Kent County, put offer on 1,000...

Mennonite families set sights on farming in Kent County, put offer on 1,000...

Unlocking Mesothelioma’s Secrets: Understanding the Tumor Microenvironment

Unlocking Mesothelioma’s Secrets: Understanding the Tumor Microenvironment

PHP with MySQL Essential Training: 1 The Basics

PHP with MySQL Essential Training: 1 The Basics

NCERT Solutions for Class 10 Maths Chapter 8 त्रिकोणमिति का परिचय 8.1 Hindi...

NCERT Solutions for Class 10 Maths Chapter 8 त्रिकोणमिति का परिचय 8.1 Hindi...


  1. Blog

    expecting assignment intouch

  2. Pin on All Weeks HomeworkFox

    expecting assignment intouch

  3. Analyze Your Assignment

    expecting assignment intouch

  4. this me

    expecting assignment intouch

  5. Assignment Expectation Pages

    expecting assignment intouch

  6. What to Expect When You’re Expecting an Assignment in South Korea

    expecting assignment intouch


  1. Toyota T-Intouch

  2. THANKS INTOUCH ฉันสาบานต่อพระพุทธเจ้า (Official Lyric Video)

  3. DIY Assignment Front Page Design 📝📜 #shorts #asmulticreativity

  4. The teacher was not expecting that 🤣


  6. Begin each day by speaking with God


  1. What Should You Expect at Basic Training at Fort Benning?

    The training experience for recruits assigned to Fort Benning varies depending upon the assigned job. Infantry and armor troops continue on at this location with their advanced training, but all recruits assigned to Fort Benning have to com...

  2. What Is the Abbreviation for “assignment”?

    According to Purdue University’s website, the abbreviation for the word “assignment” is ASSG. This is listed as a standard abbreviation within the field of information technology.

  3. What Is a Deed of Assignment?

    In real property transactions, a deed of assignment is a legal document that transfers the interest of the owner of that interest to the person to whom it is assigned, the assignee. When ownership is transferred, the deed of assignment show...

  4. Help me! About Script of Intouch9.5!

    I fill the argument into the function, but when I click the OK button, a WindowMaker warning window popup with the message"Expecting assignment"

  5. InTouch SCADA : Condition script Tutorial for Beginners

    InTouch Condition script Example for beginners visit Blog for All types of Script

  6. InTouch window script howto..

    So, you should never make a direct assignment in one, if you are going to make a decision about that assignment later. Create a memory tag, use

  7. InTouch HMI Scripting and Logic Guide

    A statement can be a value assignment, a function call, or a control structure. •. Each statement in a script must end with a semicolon

  8. Understanding How Scripts Operate in InTouch

    ... expect. The following is an example of what happens when you do this ... Q I am using the On Shutdown Application script to kill the applications

  9. Expecting assignment intouch. Web5 thg 5, 2023

    Expecting assignment intouch. Web5 thg 5, 2023 · I applied online. I interviewed at 24-7 Intouch (Bengaluru) in Jan 2023. Interview. 1 Resume screening

  10. InTouch HMI Scripting and Logic Guide

    Assignments and Operators" on page 48. Syntax Validation. When you save a script, the Script Editor automatically checks it for correct syntax. You can also

  11. InTouch User's Guide

    Expecting window name - must be string

  12. Intouch(R) Reference Guide

    expecting "0", "1". An incorrect value has been entered for a Discrete in the

  13. Wonderware® FactorySuite™ InTouch™ User's Guide

    Expecting window name - must be string expression. The given argument must be a

  14. Auto Assigning IO

    Version 1.0: The base template contained a single bit that is the trigger for all IO assignment scripts in all of the derived templates. The