Guidelines to Scripting within Industrial Application Server

Scripting is one of the key features that allow the user to truly unlock the power and versatility of Industrial Application Server.  While QuickScript.NET differs only slightly from its InTouch cousin, there are some key guidelines that a developer or integrator should consider when scripting in application objects.

I. Accessing Attributes

The term “Attribute” refers to individual properties of any object in Industrial Application server. There are several different ways to reference attributes, depending on the location of the host object.  When accessing attributes or UDAs that are within your object, you can use the “me.” construct to access them.

Ex.) On a FieldReference device, you can set the Input Reference by the following:

Me.PV.Input.InputSource = “myDIObject.MyTopic.Address”;

The “me.” Construct refers to the object you are writing the script upon. Other relative references of this nature are:

            MyContainer – References the object’s direct container

            MyEngine – References the engine the object is hosted on

            MyPlatform – References the platform the object is hosted on

            MyArea – References the area the object is hosted on

            MyHost – References the host of the object in deployment mode: 


‘Ex) In deployment view, “PWPlatform” hosts “PWAppEngine”, which hosts “Classrooms”. “Classrooms”, in turn, hosts Room 4A-4D and “RoomTemperature”.  In this example:

- MyHost of Room4A – Room4D, and RoomTemperature would be Classrooms.

- MyHost of Classrooms would be PWAppengine.

- MyHost of PWAppEngine would be PWPlatform.

When accessing attributes of other objects, there are two methods. The most common is to simply reference the attribute using its inline address.

Ex) the Input Reference of the “Boolean_001” FieldReference object would be referenced in the following string:

Boolean_001.PV.Input.InputSource

Another way to access attributes is to use the Attribute() function.  This function would be used to access attributes, particularly of DIObjects, that have characters that would be considered illegal outside of a string, such as “:” or “/”. The attribute function to call an address of “N:10/0” using a standard DDESuiteLink DIObject named “PLC1” with a topic of “ABPLC” would be:

Attribute(“PLC1.ABPLC.N:10/0”);

If this was attempted to be accessed without using the Attribute function, you would encounter an error, as the “:” and “/” are illegal characters for attribute names.

II. Asynchronous Scripting vs. Synchronous Scripting:

Another point to consider when writing code is whether you are using asynchronous or synchronous scripting. When using asynchronous Scripting, the Application Server executes the script in a set of low priority, individual threads that run independent of the main engine scan.  Thus, references to UDAs and object attributes take longer to access than the same references in a synchronous script, causing slower execution of the script if multiple calls to such attributes are made.

‘Ex) Consider the following two scripts:

Now, in a synchronous environment, option B seems not only less efficient, but also redundant, as it uses two variables to assign values to one attribute. However, in an asynchronous situation, option B will most likely execute faster. Because the script must make its references to the main engine scan while in a lower priority thread, multiple calls to attributes within an asynchronous script will actually slow the script down, therefore making multiple attribute calls within an asynchronous script likely to affect performance.

III. On-engine vs. Off-engine

Another thing to keep in mind when scripting in objects is the difference in how attribute values are evaluated, read from and written to depending on where they reside in the galaxy.  In the script, if the attribute you are writing to/reading is:

-          On-Engine (meaning the object that your script is executing from exists on the same engine as the attribute you are trying to access), or 

-          Off-engine (meaning that the object that your script is executing from exists on a DIFFERENT engine as the attribute you are trying to access)

In a given scan, an engine behaves as follows:

1)   The engine gathers information from all off-engine attributes being referenced from within the engine. This is referred to as an External Get.

2)   The engine then executes all scripts and object logic for the application objects/DIObjects that it is hosting. All attribute writes in the script that are On-Engine are written to as they are referenced. All off-engine writes are noted by the engine.

3)   At the end of the scan all off-engine attribute writes that were noted by the engine are executed by the engine. This is referred to as an External Set.

This greatly affects scripting, particularly in assignment and condition evaluation.  Consider the following example:

‘Ex)    Assume Offeng is an ApplicationObject, whose UDA “someUDA” is initially zero.

In this example, if Offeng is “on-engine” relative to the object in which this script is being run, the UDA would be assigned 1, then 2, then 3, then the targetUDA would store the value of three.  However, if Offeng is “off-engine”, then the host engine of the object running the script would make note of the change, but the final line would actually assign targetUDA a value of 0, which was what the engine collected as the value of someUDA at the beginning of the scan.  This can be easily remedied by setting the off-engine attribute references to local variables at the beginning of the script:

‘Ex)

In this example, the result would be the same, regardless if the object Offeng is on-engine or off-engine.  Let’s look at another example, this time evaluating a condition:

‘Ex) assume still that Offeng is an Application Object, where “someUDA” starts at zero.

In this situation, again, the result would differ depending on whether Offeng is on-engine or off engine.  If Offeng is on-engine, then someUDA would be set to 1 immediately, allowing for the next condition to evaluate true. If Offeng is off-engine, then the system would evaluate false, as someUDA would still be a zero as it has not been written to. Again, this can be resolved by instantiating a local variable, setting it to the value of someUDA at the beginning of the script, then write the value of the local variable back to someUDA at the end of the script:

‘Ex)

Again, this script would evaluate the same regardless of if the offeng object is on-engine or off-engine.

III. Positive Script Logic

Another topic to consider when planning your scripting is a term called positive script logic. Positive script logic involves writing conditional evaluations so that your default or “safe” case is always in the else, or false, condition.  This makes it so that your state will only change if your condition evaluates true, hence why it is called positive script logic. For example, say that we wanted to set a bit to true when a variable “a” is set to 50.  An example of positive script logic would be as follows:

‘Ex)

In Application Server, the primary reason to use positive script logic involves data quality. Data quality is passed from variable to variable; and if a variable involved in a condition evaluation is of bad quality, the evaluation fails unilaterally, and will automatically go to the “Else” condition.  Consider the two previous examples.  If “a” happened to be of bad quality, the else condition would occur in BOTH statements, but since negative logic is used in the second example, the bit would be set to one, even if the value of “a” was not 50.  This emphasizes the importance of using positive script logic in your scripting.

IV. COM/.NET Object Instantiation

One of the powerful features of Industrial Application Server is its ability to import outside COM objects, as well as being able to leverage the vast library of objects in the Microsoft .NET Framework.  Creating an object in scripting can be done two ways.  However, before that choice takes place, you need to declare a variable to store your object in. this is done in the same manner as declaring a variable of a standard type (string, discrete, etc), but you use the full inline path of the object to declare.  For example, to declare a variable to store an OLEDB Data reader, the following statement would be written:

            Dim SomeDBReader as System.Data.OLEDB.OLEDBReader;

From here, there are two choices to instantiate your COM/.NET object.  The first option is to use the CreateObject() function. 

            SomeDBReader = CreateObject(“System.Data.OLEDB.OLEDBReader”);

The other option would be to use the “new” construct as follows:

            SomeDBReader = new System.Data.OLEDB.OLEDBReader;

The difference between these two is that the former is what is considered a “late-bound” object, where the latter is considered an “early-bound” object. Late-bound objects are not validated by the script validator, and the platform where the object is running must have the COM library (.NET or an imported DLL or TLB file) installed upon it for the object to instantiate.  Conversely, “Early-bound” objects ARE validated by the script validator, and only need to be pre-installed into the Galaxy using the import script function utility.

The advantages of using early-bound object instantiation are clear; you do not need to install your objects on individual machines, and you have the ability to validate your script.  For example, if after you instantiate your object you attempt to call an invalid function of that object:

‘ex)

            SomeDBReader.GoAway();

If you instantiated your variable using the CreateObject() function, the script will validate without telling you it the call is invalid.  You would only find out that the call is invalid when you attempted to run your script and the script failed.  Conversely, the script would not validate and would give you an invalid reference error if you instantiated your variable using the “new” construct.  Hence, unless you have a COM object that you want to install on only certain platforms, it is a good idea to use the “new” construct.

Summary:

I.          Attributes can be referenced relationally by using the “me” or similar reference keywords, or directly by using their inline address or by using the Attribute() function.

II.         When writing asynchronous scripts, it is a good idea to “buffer” your Attributes using local variables, then write back to them at the end of the script for execution time purposes.

III.       When using off-engine references, it is essential to “buffer” your attributes using local variables using local variables in order to assure correct conditional evaluation.

IV.        Positive script logic will ensure that a condition will not be falsely set due to bad data quality.

V.         Instantiating COM and .NET objects using the “new” construct allows the script validator to evaluate COM function calls and only requires you to install the COM library within the galaxy.  Using the CreateObject() function allows you to install your COM object only on the target platform, but loses the ability to validate your COM function calls from within the script editor.

 

©2003 Q-mation, Inc. All rights reserved. All trademarks are the property of their respective owners.