Вы находитесь на странице: 1из 160

Charles

Vance
1
2
Copyright © 2019 by Charles Vance

All rights reserved. No part of this publication may be reproduced, stored
or transmitted in any form or by any means, electronic, mechanical,
photocopying, recording, scanning, or otherwise without written
permission from the publisher. It is illegal to copy this book, post it to a
website, or distribute it by any other means without permission.

Charles Vance has no responsibility for the persistence or accuracy of
URLs for external or third-party Internet Websites referred to in this
publication and does not guarantee that any content on such Websites is,
or will remain, accurate or appropriate.

Designations used by companies to distinguish their products are often
claimed as trademarks. All brand names and product names used in this
book and on its cover are trade names, service marks, trademarks and
registered trademarks of their respective owners. The publishers and the
book are not associated with any product or vendor mentioned in this
book. None of the companies referenced within the book have endorsed
the book.

First edition

3
4
STEP BY STEP
ARCHESTRA
SCRIPTING

Charles Vance

5
6
INTRODUCTION

Welcome to Step By Step Archestra Scripting! The fact that you are
reading this right now proves you want to improve your programming
abilities as an Archestra programmer/developer. I built a career and a
business programming and developing System Platform galaxies for
major companies primarily in the energy sector. This product is used in
many industries and whatever industry you choose to work in will likely
have this product installed in one form or another to manage its
machines or processes.

it's my desire to impart some of the many years of experience I have
gained to you, my hope is to provide you with the tools you need to excel
in your role as a programmer or developer. In this book I will focus on
scripting and show you how to use Microsoft Visual Studio to extend
Archestra capabilities using the C# language to compile dynamic link
libraries (DLL’s) that are imported to become Archestra script libraries.
Finally, I will show you how to make your own .Net client controls such as
data grids and other animations found in .Net to do specialty functions
not available for use in the product any other way. Once you have
unlocked the power of using .Net controls any of the controls in Dot Net
are available for you to use in a galaxy!

7
Additionally, I will provide bonus material including how to use the two
custom component libraries GRaccess and MXAccess. Using GRAccess
enables you to write powerful application programmatically leveraging
functionality in the Integrated Development Editor such as galaxy backup,
object creation, uda manipulation and object deployment. MXAccess
opens the door to the creation of your own custom applications to read
and write galaxy objects using the LMX data transport layer.

Other resources to this book is a companion online course and
community forum designed to assist you in making these concepts easier
to grasp as you work your way through the examples and tutorials in this
book. You can find more information about the online course and
community forum at www.charlesbvance.com

I attempted to reach a broad scope of reader from the very basic through
advanced, if you find yourself reading topics you have already mastered
feel free to skip ahead as I have designed the book to grow in complexity
as you advance the pages. Working the tutorials will cement the concepts
in your mind making it easier for you to code similar methods and
functions later on. I learn best when I put action to my reading and I hope
that method serves you too.

Although the scope of this book is to teach you how to be a better


scripting programmer. I am assuming you have a basic understanding of
how to configure platforms, engines and objects for deployment.

However, because we need to set the stage for the scripting I will be
demonstrating in these tutorials I am including an explanation of the
galaxy configuration I used when doing testing of these scripts.

8
For information about how to setup a basic galaxy please go review the
Free Archestra Course I created by clicking https://
archestrabasics.teachable.com/

The basic galaxy setup for the tutorials and examples in this book
consists of the Galaxy Repository, a deployed platform and various
objects deployed both on the GR and remote platform.

This setup is similar to the basic galaxy used in the free Archestra course
I offer.

These scripting tutorials are very basic and typically I will use the object
viewer when testing these scripts except for later examples where we will
use the open source Modbus simulator driven by VBS code and
FSGateway to communicate with galaxy objects via the OPC DI Object.

As previously mentioned the teachings are designed to become


progressively more difficult, please feel free to skip ahead if you already
know how to do what is being discussed.

In these examples I have deployed the Intouch view application to the GR


for simplicity and will deliberately keep the galaxy architecture simple as
the purpose of this book is to teach scripting rather than how to configure
galaxy platforms, engine and objects. The illustration below is the
deployment view of my test galaxy and on the next page you will find an
architecture diagram depicting the layout of this test galaxy.

You may use any configuration you have available when working out
these examples I am just letting you know how I set my galaxy up.

9
10
11
Basic Galaxy Concepts

The galaxy is everything contained in the galaxy repository as viewed


from within the IDE.

It represents a single name space for all deployed objects and graphic
symbols which allows us to use object references when connecting
instances to each other or to a graphic symbol for animation or
visualization.

The platform is a construct designed to associate with a physical


computer or device. The first platform created within the IDE is always the
GR (Galaxy Repository) platform and represents the physical server
where all galaxy information is stored and deployed from.

Each subsequent platform created within the IDE represents an additional


physical server or computer where engines and areas will be deployed to
and be hosted at runtime.

The platform contains properties that report its condition regarding


loading and performance as well as many other diagnostic indicators.

Before a platform object is deployed to a physical target computer the


“boot strap” software must first be installed. The bootstrap contains
initialization code for loading and managing platform operations.

Engines and Areas are key operational elements to understanding


platform operations. Engines are special objects that are deployed to
platforms and control the scan rate and execution order of scripts and
other communication functions.

With the exception of device integration objects (DIO) only area objects
may be assigned directly to an engine meaning that all other objects must
be assigned to an area object prior to deployment. As you will see later,

12
the area object is part of the “model view” of the galaxy providing a
pathway for alarm information to be collected and displayed.

Containment is a concept where an object is assigned to a higher level


object for isolation and organization. It provides a mechanism for creating
custom objects that model the real world. For example, a valve container
might have an open limit, close limit and command objects contained
underneath it which are then accessed using contained names.

Application objects are all other objects and typically will contain field or
other attributes connected via the input source of the attribute to other
objects, internal scripts or special objects designed to connect to PLC’s
and RTU’s called Device Integration Objects.

Wonderware provides many types of DI Objects for communicating with


various data protocols such as Modbus, Data Highway, OPC, etc., some
DI Objects are actually clients designed to connect with 3rd party protocol
adapter programs.

Attributes are the lowest level elements that each object may contain.
These attributes may be system derived that will exist in every object or it
could be a user defined attribute (UDA) with or without real world
interface extensions applied.

Scripts can reference these attributes for internal calculation or decision


processing. The object attribute is similar to the tagname in legacy
Wonderware with the exception it can be configured as either local or
global in scope where legacy tags in Wonderware are always global.

The illustration on the next two pages show the basic components of the
Integrated Development Environment where objects and graphics are
created, modified and deployed as well as the basic component model of
Archestra.

13
14
15
16
CHAPTER 1
NATIVE SCRIPTING

When I use the phrase “Native Scripting” I am referring to the scripting


language built-in to the galaxy which is similar to the C sharp language
but doesn’t have all of the same capabilities that C sharp does. The
scripting compiler included in the product is very versatile and will fit most
of your needs but there are certain cases where it just can’t accomplish
the task at hand, fortunately Wonderware provided a mechanism for use
of a more powerful or flexible external language use through Dot Net
Framework extensions, something we will cover in part two of this book in
the chapters about custom scripting.

Vendor Documentation

Wonderware has created a very robust document library for developing,


programming and supporting the System Platform product suite. The
entire document set can be installed during the installation of System
Platform and I strongly urge you to do so.

If the option to install the documentation included within the product was
selected, the documents can be found in the “Wonderware/Books”
section of the Windows Start menu providing an in-depth understanding
of how things work. The two primary documents we will be referencing as
we work through the tutorials in this section of the book are Application
Server Scripting Guide and the Intouch Scripting and Logic Guide.

17
All of the documents referenced in this book are available for download at
the AVEVA software support website located here https://
softwaresupport.aveva.com you will need to create an account if you
haven’t done so already.

Unless you have a customer first account through your company, most of
the information and downloads will not be available to you but the
documents I am referencing are available for public download to anyone
with an account. If you are purely an academic learner and do not have a
company name to use in the online access request try using the name of
your school or other academic organization you are affiliated with.

Other documents we will refer to are the Wonderware Technical Notes


addressing the management and use of dynamic link libraries (DLL’s) for
Custom Libraries (aaSLIB) and Dot Net Custom Client Controls. While
the Tech Notes are not available for public download I will summarize and
distill the most pertinent aspects of the information into the section
explaining use of Custom Libraries.

Basic Script Structures

Wonderware vendor documentation describes all of the constructs


discussed in this section therefore this is not an attempt to repeat every
one of them rather this is a deep dive into the more commonly used of
the operational constructions describing how each is used from my
perspective.

he Wonderware Application Server Scripting Guide is the “GO TO”


T
reference when writing scripts in galaxy symbols and objects and is
where you will find everything you need to understand the built-in or
“basic” scripting language inside Archestra. You can find the guide either
in the Book section of the Wonderware installation directory or from the
start menu when executing the IDE. If you have any issues locating the
document it can also be downloaded from the AVEVA support website for

18
developers, if you haven’t done so already, please go now and register
for a support account. Directions for doing so can be found in a video on
my YouTube channel here.

Once you have located the scripting guide, a quick scan of the table of
contents reveals that it addresses Common Scripting Environment,
QuickScript Dot Net Functions and includes a rich set of sample scripts,
some of which we will deep dive when we get to the tutorial portion of the
text. For a complete understanding of expression syntax refer to the
guide.

Archestra scripting is primarily done in the graphic symbol runtime


behavior collection property or object template script area but scripts may
also be written in the action animation on any element that supports that
animation. Graphic symbol collection or element animation script
execution begins once the symbol is shown based on the trigger type
assigned at that level.

Wonderware System Platform object template scripts are written in the


template Script area based on its configured execution and trigger type.

The five script execution types available in the object template are:

• Startup
• OnScan
• Execute
• OffScan
• Shutdown

Execute type scripts are the most common execution type, this type is
activated every time the object is scanned if the object is in the “OnScan”
state. Other execution types are startup, OnScan, OffScan,shutdown,
and deployment with each one having its own specific purpose as
described in detail in the Wonderware guide.

19
An important operational characteristic to consider when using the
execute script is to wait a few scans after object deployment before
allowing the script to evaluate anything. This prevents needless run time
errors providing more robust performance of the script, you will see
examples of this type of coding in the examples provided.
In addition to the various execution types another construct is script
Trigger Type.

The six trigger types are:

• Periodic
• Data Change
• OnTrue
• OnFalse
• WhileTrue
• WhileFalse

DataChange scripts execute anytime a data value or quality status


changes between scans which means you will always get at least one
execution of the script without unnecessary scans which increases
performance. WhileTrue scripts, if used correctly guarantee execution of
the script logic, unfortunately OnTrue and OnFalse are known to have
issues with missing execution cycles. I’m not sure if this has been
corrected in version 2017 and beyond but I will always use a WhileTrue
or WhileFalse execution trigger in combination with a Boolean trigger to
ensure firing of the script, this method will be demonstrated in detail later
on.
Logic Structures and Example Scripts
There are 5 basic structures used in the built-in language to control logic
flow, in this section we will describe all of them and provide examples of
how each is used.

20
The structures addressed in this section are:

IF-THEN conditional execution


FOR-NEXT looping structure
FOR EACH looping statement
WHILE looping structure
TRY-CATCH error handling conditional.

Each structure provides a different functionality and purpose beginning


with the IF-THEN-ELSE-ELSEIF which can actually be broken down into
3 sub-structures, IF-THEN, IF-THEN-ELSE, and IF-THEN-ELSE-ELSEIF.
Note that each of these condition blocks end with an ENDIF statement
and when multiple ELSE IF conditions are used an additional ENDIF
must be used. The IF-THEN statement executes based on condition
between the IF and THEN statements bounded by the ENDIF statement.

The IF statement is one of the most commonly used conditional


statements, the condition after the IF keyword is evaluated and the line
following the THEN keyword executes if the condition is true. If the
condition is not evaluated as true the line is skipped and execution ends
unless the ELSE or ELSEIF statements are included in the control
structure. If there is an ELSE keyword and the condition after the IF is
false the line after the ELSE statement executes.

If an ELSE IF is present it is evaluated in order, the IF first than each


subsequent ELSEIF and finally the ELSE is always last in the chain
although an ELSE is not required. The ENDIF keyword is always
required.

When using basic scripts the condition variable may be a hard-coded


object reference, a custom property of the symbol or object, or an
internal dimensioned variable.

21
I have created two objects for use when hard-coded references are
needed to demonstrate in the script examples demonstrated a little later
in this section, a third object used is the Master Trigger object containing
trigger UDAs for each example script.

The use of internally dimensioned variables will be made clear in the


script itself. The two tables on the next page describe the objects and
attribute names we will be referencing in our examples.

All of the examples demonstrating the basic constructs are configured in


the object script editor as Execute type scripts triggering as a "While
True" script meaning as long as the triggering expression is true the
script will continue to execute. For consistency I have created example
trigger UDAs in the master object that I will set to true using the object
viewer.

For example, the first script example uses MasterObject.Trigger1,


example 2 uses MasterObject.Trigger2 and so on for all 14 examples. I
will continue using this structure for script testing throughout the book.

The illustration on page 26 shows how the actual Archestra Script editor
and its settings appear, however instead of providing pictures for each
script for clarity sake I will describe the setup in text format as follows:

22
23
Execution Type: Execute
Trigger type: WhileTrue
Expression: MasterObject.trigger1
1. '###############################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex1 demonstrate IF-THEN
5. '###############################
6. 'dimension variables
7. dim a as boolean;
8. dim b as integer;
9. 'assign values from external reference
10. a = Me.boolTest;
11. 'if variable a is true execute
12. if a then
13. b = 3; 'set internal variable to 3
14. endif;
15. 'assign external uda to internal variable
16. Me.integerTest = b;
17. 'end script execution
18. MasterObject.trigger1 = false;

Every script example is stored in the cloud in a GitHub repository a link to


the code example will be provided so you can go there and download it.

Example 1: IF-THEN-ELSE
Conditional statements are the glue holding everything together providing
a framework for automated decision making. Every action of the operator
can be evaluated and program flow directed based on that evaluation. At
the most basic level the IF-THEN-ELSE statement makes automation
possible.

24
The flow chart diagram at the bottom of the page describes the basic IF-
THEN statement, if the condition is met the statements associated
execute otherwise the logic flow ends without any execution.

Converting the logic design of the flow chart diagram to actual code
becomes a simple task of matching the instruction set of whatever
language we are coding in.

The flow chart diagram can be translated into the built in script language
as shown in the object script editor on the next page.

IF-THEN Flow Chart

25
This is how the example 1 script will look in the Archestra script editor but
as already explained for clarity I will simply place the script in text format
with the type and trigger along with a link to the GitHub repository where
the full script can be downloaded.

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example1

26
Example 1 IF-THEN Code Explanation
(Note: Compare line numbers in the text script example not the
illustration)

Line 1-5
Brief description of the code segment a best practice with big payoffs in
the future. While this practice may seem to be a waste of time especially
for simple code, you will discover that returning to a code segment
months or even years later having a simple description and well
documented code will assist you in figuring out what it means and how it
works long after you have completely forgotten about it.

Line 6
Brief description identifying this section as the place where variables are
defined.

Line 7 & 8
Dimension variable “a” as Boolean and variable “b” as integer, these are
internal variables that exist only within the scope of the script, great for
doing “throw-away” calculations.

Line 9
Describes the action to follow, in this case assignment of data from an
external reference, an object User Defined Attribute (UDA), into an
internally dimensioned variable.

Line 11
Describes what will happen in the IF-THEN statement if the conditional
variable is a particular state. Since this is a Boolean it isn’t necessary to
use an evaluation operator such as equal-to, all that is needed is to state
the variable.

Line 12
Is the actual conditional statement evaluating the internal variable and

27
when its true line 13 is executed setting internal variable b to a 3. Line 14
terminates the if statement, the ENDIF keyword is always required when
using the IF-THEN statement.

Line 15
Is a comment to describe the action to follow and line 16 is the
assignment transferring contents of the internal variable to an externally
UDA.

Line 17
Describes Line 18 where the triggering UDA is set to false in order to stop
execution of the script.

Line 18
I choose to use the WhileTrue execution type that executes until the
trigger is set false rather than the OnTrue simply because I have had
scenarios in the past where an OnTrue script didn’t trigger properly, I
have never had a script fail to execute on a trigger condition using this
method so i decided to make it a habit any time I need a one shot
execution.

Example 2: IF-THEN-ELSE-ENDIF

The IF-THEN-ELSE statement operates exactly the same way as the IF-
THEN with one exception, instead of terminating execution when the IF
statement isn’t satisfied it executes an alternative statement. This
structure is excellent for building toggles.

In the flow chart below you can see the logic flow of the toggle. A toggle
script should be one of the simplest things you can program yet you might
be surprised to learn it’s just as easy to get into a situation where we over
program and write many lines of redundant code to accomplish a simple
task. If a simple toggle seems to take more than a few lines of code stop
what you are doing and start over.

28
IF-THEN-ELSE Flow Chart

29
In the logic flow for a toggle we look to see if the Boolean is true or not, if
not make it true, otherwise make it false. In this way each time the script
executes it will reverse the state of the Boolean.

Example 2 Script

Execution Type: Execute


Trigger Type: WhileTrue
Expression: MasterObject.trigger2

1. '####################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex2 demonstrate IF-THEN-ELSE
5. '####################################
6.
7. 'dimension variables
8. dim a as boolean;
9.
10. 'assign values from external reference
11. a = Me.udaboolTest;
12.
13. 'if variable a is true make it false
14. if a then
15. a = false;
16. else a = true; 'if variable a is false make it true
17. endif;
18.
19. 'assign contents of a to external reference
20. Me.udaboolTest = a;
21.
22. 'end script execution
23. MasterObject.trigger2 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example2

30
Example 2: IF-THEN-ELSE Code Explanation

Line 1-6
A brief description of the script including date and author, this will be
helpful when we return later or for others to understand what we
intended.

Line 8-10
Comment your code and dimension the variables used in the script,
dimensioned variables need to be assigned a value to initialize them
to a default position.

Line 14-18
Is the actual conditional statement evaluating the variable. If the
condition is true we set the variable false, otherwise we set it true.

Line 20,21
Set the external object uda to the same value as the internal
variable.

Line 23,24
Set the trigger false to stop execution of the script.

31
Example 3: IF-THEN-ELSEIF-ENDIF

The IF-THEN-ELSEIF logic flow continues to an alternative condition if the


primary condition fails. In this example there is a single alternative
condition but additional alternatives and a final alternative is possible as
you will see in the final example for this structure a bit later on. The
operation of the Elseif and the Else are identical but we use the Else
instruction at the end of a series of Elseif statements as a default state.

IF-THEN-ELSEIF Flow Chart

32
Example 3 Script

Execution Type: Execute


Trigger Type: WhileTrue
Expression: MasterObject.trigger3

1.
2. '####################################
3. 'Author: Charles Vance
4. 'Date: 08/08/2019
5. 'Script: ex3 demonstrate IF-THEN-ELSEIF
6. '####################################
7.
8. 'dimension variables
9. dim a as boolean;
10. dim b as boolean;
11.
12.
13. 'assign values from external reference
14. a = Me.udaboolTest;
15. b = Object2.udaboolTest;
16.
17. 'if variable a is true make it false
18. if a then
19. a = false;
20. 'alternative if sets a to value of b if b is true
21. else if b == true then
22. a = b;
23. endif;
24. endif;
25.
26. 'assign contents of a to external reference
27. Me.udaboolTest = a;
28.
29. 'end script execution
30. MasterObject.trigger3 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example3

33
Example 3: IF-THEN-ELSEIF Code Explanation

Line 1-6
A brief description of the script including date and author, this will be
helpful when we return later or for others to understand what we
intended.

Line 8-10
Comment your code and dimension the variables used in the script,
dimensioned variables need to be assigned a value to initialize them to a
default position.

Line 13-15
Is the actual conditional statement evaluating the variable. If the condition
is true we set the variable false, otherwise we set it equal to our external
uda.

Line 20,21
Set the original external object uda to the same value as the resulting
internal variable.

Line 23,24
Set the trigger false to stop execution of the script.

Example 4: IF-THEN-ELSEIF-ELSEIF…..ELSE-ENDIF

The IF-THEN-ELSEIF logic flow can continue as many times as needed


to cover many different possible alternatives to the primary condition.

Always keep in mind the need for simplicity when considering using
multiple ELSEIF branches though as the code can become complex and
difficult to troubleshoot later on.

34
Making lots of comments in your code is a great practice in these
situations, it might also be just as easy to make two successive IF-THEN
statements instead of an ELSEIF.

The Flow Chart below shows how a string of ELSEIF branches ending
with a final ELSE statement can be created to cover a range of possible
outcomes for the script.

The ELSE statement at the end is used as a “catch-all” to address


anything unexpected.

Multiple ELSEIF Flow Chart

35
Execution Type: Execute
Trigger Type: WhileTrue
Expression: MasterObject.trigger4

1. '##############################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex4 demonstrate IF-THEN-ELSEIF(3)-ELSE
5. '##############################################
6. 'dimension variables
7. dim a as boolean;
8. dim b as integer;
9. 'assign values from external reference
10. a = Me.udaboolTest;
11. b = Object2.udaintegerTest;
12.
13. 'if variable a is true make it false
14. if a then
15. a = false;
16. 'alternative ELSEIF (1) sets a true if b is 1
17. else if b == 1 then
18. a = true;
19. 'alternative ELSEIF (2) sets a false if b is 2
20. else if b == 2 then
21. a = false;
22. 'alternative ELSEIF (3) sets a true if b is 3
23. else if b == 3 then
24. a = true;
25. else a = true; 'if variable a is false make it true
26. endif;
27. endif;
28. endif;
29. endif;
30.
31. 'assign contents of a to external reference
32. Me.udaboolTest = a;
33.
34. 'end script execution
35. MasterObject.trigger4 = false;
GitHub Code Link:
https://github.com/cbvance/StepByStep/blob/master/Example4
36
Example 4: IF-THEN-ELSEIF-ELSEIF…ELSE Code Explanation

Line 1-5
A brief description of the script including date and author, this will be helpful
when we return later or for others to understand what we intended.

Line 6-12
Comment your code and dimension the variables used in the script,
dimensioned variables need to be assigned a value to initialize them to a
default position.

Line 13-29
The core of this script example is the use of multiple IfThen-Elseif
structures to evaluate a successive series of conditions to establish several
different potential results depending on what the input condition is. This
example shows you that you can nest as many of these alternative
conditional statements as you want but remember to keep it simple.

Line 31-35
The rest of the script is cleanup by assigning the internal variable to the
external uda and setting trigger false to end execution of the script.

37
Example 5: FOR-TO-STEP-NEXT

The FOR-NEXT Loop is used to loop a set of instructions either counting


up or counting down depending on how the TO statement is configured.
When the Start expression is larger than the end expression the loop
counts up otherwise it counts down.

FOR-TO-NEXT Flow Chart

38
Execution Type: Execute
Trigger Type: WhileTrue
Expression: MasterObject.trigger5

1.
2. '##############################################
3. 'Author: Charles Vance
4. 'Date: 08/08/2019
5. 'Script: ex5 demonstrate Incrementing FOR-STEP-NEXT
6. '##############################################
7.
8. dim index as integer;
9.
10. for index = 1 to 9 step 1
11.
12. LogMessage("index = " + index);
13.
14. next;
15.
16.
17.
18.
19. 'end script execution
20. MasterObject.trigger5 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example5

Example 5: FOR-TO-STEP-NEXT Code Explanation

Line 1-6
A brief description of the script including date and author, this will be
helpful when we return later or for others to understand what we intended.

Line 8
Dimension an internal variable for indexing.

39
Line 10-14
The for statement always has a starting place and an ending place in
this example we start with 1 and end with 9 our step increment in this
example is 1 meaning each step through the loop will be by 1 until we
reach 9. Since the starting number is smaller than the ending number
we are counting up, if the starting number was 9 and the ending
number 1 we would count down through each successive loop.

Line 19,20
Add a comment and set the trigger to false to stop execution of the
script.

Example 5b: FOR-EACH-IN-NEXT

The FOR EACH works in exactly the same way as the FOR-NEXT
except it is only to be used with collection objects in an OLE (Object
Linking Embedded) Automation Server.

Example 6: WHILE-ENDWHILE LOOP

The While loop statement allows us to loop through any set of


instructions until an end conditions is satisfied. This is perfect for
opening and reading files or any other operation that requires multiple
execution of the same task or set of tasks.

The code example for our WHILE Loop demonstrates how to open
and read a text file line by line until the end of file is reached.

40
WHILE-ENDWHILE Loop Flow Chart

41
Execution Type: Execute
Trigger Type: WhileTrue
Expression: MasterObject.trigger6

1. '##############################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex6 demonstrate WHILE ENDWHILE Loop
5. '##############################################
6.
7. 'Access the dot net stream reader for file reading
8. dim reader as System.IO.StreamReader;
9.
10. 'configure the reader to open our eaxmpolae file
11. reader = System.IO.File.OpenText("C:\Ex6File.txt");
12.
13. 'read one line each loop until the end of the file is reached
14. while reader.Peek() > -1
15.
16. LogMessage(reader.ReadLine()); 'write the contents to the logger
17.
18. endwhile;
19.
20. 'close the reader instance
21. reader.Close();
22.
23. 'stop execution of the script
24.
25. MasterObject.trigger6 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example6

For this example I will include an image of the contents of the logger file
to demonstrate what the script does.

42
Result of File Read in the Logger

Example 6: WHILE-ENDWHILE-LOOP Code Explanation

Line 1-6
A brief description of the script including date and author, this will be
helpful when we return later or for others to understand what we
intended.

Line 7,8
Open the dot net stream reader object to read the file contents and use
a comment to document what we are doing.

Line 13-18
Use the while loop to step through the file line by line outputting each
line to the logger until we reach the end of the file.

Line 20,21
Close the reader instance when we are finished to make sure we don’t
create a memory leak.

Line 23-25
Set the trigger to false stopping script execution.

43
Example 7: TRY-CATCH-ENDTRY

The final statement type we are going to examine is the TRY-CATCH


structure. This structure is used for error handling.

The TRY instruction is placed before any other scripting instructions and
the ENDTRY is placed at the end of the script section you want to test for
errors.

The CATCH instruction is placed between the TRY and ENDTRY to


execute if an error is detected. You can use the Catch statements to
report the error using the “error” reserved keyword, attempt to fix the error
condition, perform a graceful exit, or ignore the error altogether.

Ignoring the error is useful when you are testing but is not a good idea to
leave in place after the code is completed.

TRY-CATCH-ENDTRY Flow Chart


44
In the following code example for using the try catch error handler I used
our file read sample code. I changed the name of the file in my C drive to
force a file read error.

Normally this type of error will cause some pretty ugly error messages as
the script locks up flooding the logger as seen in the first logger
illustration. After the try catch structure is inserted though we see a single
warning message and the script is shut down by the try catch, the second
logger illustration shows how the logger looks when using the error
handling code. It is a best practice to use the try catch it will make your
code more robust and fault tolerant.

Example of logger errors without error handling

Example of logger with error handling

45
Execution Type: Execute
Trigger Type: WhileTrue
Expression: WhileTrue

1. '##############################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex7 demonstrate TRY CATCH - ENDTRY
5. '##############################################
6.
7. 'implement the Try Catch error handling structure
8. try
9.
10. 'Access the dot net stream reader for file reading
11. dim reader as System.IO.StreamReader;
12.
13.
14. 'configure the reader to open our eaxmpolae file
15. reader = System.IO.File.OpenText("C:\Ex6File.txt");
16.
17.
18. 'read one line each loop until the end of the file is reached
19. while reader.Peek() > -1
20.
21. LogMessage(reader.ReadLine()); 'write the contents to teh logger
22.
23. endwhile;
24.
25. 'close the reader instance
26. reader.Close();
27.
28. 'stop execution of the script
29.
30. MasterObject.trigger7 = false;
31.
32. catch
33. LogWarning(error); 'write the contents to the logger
34. MasterObject.trigger7 = false;
35. endtry;

GitHub Code Link:

46
https://github.com/cbvance/StepByStep/blob/master/Example7

Example 7: TRY-CATCH-ENDTRY Code Explanation

Line 1-6
A brief description of the script including date and author, this will be
helpful when we return later or for others to understand what we
intended.

Line 7,8
Place the try instruction at the top of the code to enclose everything in the
script, This ensures that we check all operations when the script
executes.

Line 10-30
Same as our While loop code example.

Line 32-35
The catch statement is where we do our error handling. In this example
we are using the LogWarning function to create a logger message that is
yellow that displays the error message returned from the try statement.
We will also stop execution of the script when there is an error.

47
Common Functions

DText()

This function returns a text string corresponding to the state of the


boolean attribute passed to it. This is very useful when you have a
discrete value and need a text message displayed depending on its state
value.

In our example we will create a graphic symbol that will display a text
message depending on the state of the discrete custom property
associated.

Step 1 - Create the Symbol

48
Step 2 Add Custom Properties

We will add two custom properties as follows:

Property Name | Message | Data Type | String | Default Value | (Blank) |


Visibility | Private

Property Name | OnMsg | Data Type | String | Default Value | ON |


Visibility | Public

Property Name | OgffMsg | Data Type | String | Default Value | OFF |


Visibility | Public

Property Name | Value | Data Type | Boolean | Default Value | me.Value |


Visibility | Public

49
Step 3 - Add Animations

Adding animation is easy to do just double click the element where the
animation is going to be applied and the animation dialog pane will pop
up, at the upper left is the Animation + - option. Clicking the + icon reveals
the animation toolbox with all of the options available. Clicking the - icon
will remove the selected animation.

In this example we select the Value Display animation choosing String


State and then begin typing our property name “Message”, the property
name will appear and we can click to select it.

Step 4 - Add the Scripting



Adding scripting is the final step to make this display symbol operate
correctly. We are going to create a script that executes every time the
input Boolean value changes.

50
To do this we will create a DataChange script based on our custom
property “value”.

Each time the script executes the DText function will evaluate the state
of the input parameter placing the result message in our “Message”
custom property which is then displayed on our symbol.

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example8

To use the symbol there must be a corresponding object with a Boolean


attribute named “value”. Drop the symbol onto either an intouch window
or another symbol making sure to assign the Owning Object property to
the correct object.

Now when the state of the Boolean value changes the display will update
with whatever is in the OnMsg and OffMsg strings.

51
LogMessage() LogWarning() & LogError()

The ability to write custom messages to the logger is extremely valuable


and an important tool when troubleshooting your scripts. Information
entries into the log will tell you if a script is behaving the way you expected
it to and can also be useful for leaving bread crumbs when trying to
understand how a function is working.

When using the try catch method for error handling the log warning or log
error will help you determine what the problem is. Typically the
LogMessage is used when the information is diagnostic but not anything
to worry about, a warning message in the logger is something that is not
operating as expected but may not require immediate attention, Errors on
the other hand are things that are serious requiring immediate attention.

Execution Type: Execute


Trigger Type: WhileTrue
Expression: MasterObject.trigger9

1. '##############################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex9 demonstrate Log Messages
5. '##############################################
6.
7.
8. 'use a few empty messages just to make some space
9. LogMessage("");
10. LogMessage("");
11. LogMessage("");
12.
13.
14. 'LogMessage() displays custom information to logger without color
15.
16. LogMessage("This is an axample of a Message");

52
17. LogMessage("");
18.
19. 'LogWarning() displays a custom warning message as yellow text in the logger
20.
21. LogWarning("Ths is an example of a warning");
22. LogMessage("");
23.
24. 'LogError() displays a custom error message in red to the logger
25.
26. LogError("Ths is an example of an error");
27. LogMessage("");
28.
29. MasterObject.trigger9 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example9

Example of logger after running script

53
BindTo Method and Associated Functions

Sometimes it’s necessary to access multiple objects and attributes and
runtime where we don;t have access to the name of the objects at design
time requiring a method for dynamic binding.

This is the purpose of the BindTo method when using indirect tag types.
This binding method only works in objects and cannot be used in
Archestra symbols. When we need to do dynamic binding in an Archestra
symbol we will use the SetCustomProperty function.

The SetCustomProperty function will be covered later in the GraphicInfo


Client section. Another limitation of the BindTo referencing method is that
it may only be used when the object is deployed to the same engine
where it’s referenced. In other words objects on one engine cannot
dynamically bind to attributes on a different engine, this is sometimes
referred to as “cross application, or “Cross engine” binding.

Example 10 Code Sample

Execution Type: Execute


Trigger Type: WhileTrue
Expression: MasterObject.trigger10

1. '##############################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex10 demonstrate use of BindTo to
5. ' dynamically bind object refeences at runtime
6. '##############################################
7. 'variable dimensioning and assignmen
8. dim x1 as indirect;
9. dim x2 as indirect;
10. dim source as string;

54
11. dim obj1 as string;
12. dim obj2 as string;
13.
14. obj1 = "Object1";
15. obj2 = "Object2";
16.
17. 'make sure the object has enough time to connect to galaxy
18. if Me.Ex10.ExecutionCnt > 2 then
19.
20. 'bind object 1 reference
21. source = obj1 + ".udaboolTest";
22. x1.BindTo(source);
23.
24. 'bind object 2 reference
25. source = obj2 + ".udaboolTest";
26. x2.BindTo(source);
27.
28. 'set object 1 same as object 2
29. x1 = x2;
30.
31. 'stop script execuiton
32. MasterObject.trigger10 = false;
33.
34. 'end of conditional
35. endif;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example10

Example 10 Code Explanation

In example 10 we are binding two objects to an indirect than setting one


object to the value of the other. The script resides in object 1 then uses

55
dynamic binding to create a reference to a local attribute and an attribute
on the remote object. Both object MUST be deployed to the same engine
for this to work.

Line 1-6
A brief description of the script including date and author, this will be
helpful when we return later or for others to understand what we intended.

Line 7-16
Dimension the variables and assign object names to the source variables.

Line 17,18
It’s a good idea to let the object settle out after deployment before
attempting dynamic reference binding. This condition statement waits
until the script has gone through a couple of execution cycles before
allowing the binding to occur.

Line 20-29
The source variable is set to the full object reference then passed to the
BindTo method for object 1 and object 2. Once the two indirect values x1
and x2 have been bound to the object attributes we can set X2 equal to
the value of x1.

Line 31-35
The remainder of the code stops execution of the script and closes the if
condition.

Functionality of the code in example 10 can be tested using the object


viewer as configured in the illustration below. The script execution count
can be seen as starting at zero after each deployment cycle. When the

56
trigger is set to true the script begins execution then after the script
executes a couple of times the attribute of object 1 is set to whatever the
state of object two is.

In the two illustrations below I show you what the state of the attributes
before the script is run and after. Notice that the script has executed 4
times and Object1.udaboolTest is false.

57
String Handling

String handling covers the basics of manipulating strings of text and data
in order to provide dynamic functionality. We use strings of text to display
names of equipment, loop identification, process areas and many other
things.

We also use strings of text and data to create IO binding references for
object input sources and other dynamic object referencing. The ability to
split, slice, and dice strings at runtime is a critically important
programming skill.

A complete discussion of the details of each of the string functions


available in Archestra System Platform native script language is
presented in the Archestra Application Server Scripting Guide.

The scope of this book is to explain and demonstrate how the string
handling functions are used when visualizing processes or machinery. If
you have questions about the specific detail of a particular function please
reference chapter 2 of the Archestra scripting guide pages 76 - 94.

The scripting guide does a good job of providing examples for each of the
uses of the string functions so I won’t attempt to duplicate them, the
scope of this book is to show you common ways these functions are used
when scripting objects and graphics.

The following examples illustrate a few of the ways strings are used.

ASCII Code Translations

Integer Result = StringASCII(’Char’) - returns the ACSII code value of the


first character in the character parameter passed into the function.

58
String Result = StringChar(ASCII CODE) - returns the Character
corresponding to the ASCII code value passed into the function.

Typical uses for ASCII codes in industry are transmitting information to


external devices such as ink jet printers or other embedded processing
equipment with limited compute power.

As technology improves the need for this level of granularity is becoming


less and less (Raspberry PI is an example of a powerful low cost
embedded computing device capable of string handling without ASCII
conversion). It’s still good to know though just in case you need to decode
a string of ASCII code numbers into readable text.

String Search and Replacement

While there are a lot of string manipulation functions, the two main areas I
find myself using most are string splitting and conversion of a string to a
number or a number to a string. The Wonderware scripting guide is the
best source for how each function is defined so I won’t repeat everyone
of them here, what I hope to accomplish is to highlight the common
functions providing examples of each.

First I’ll explain the replace string function, StringReplace(), the three
main string splitting functions, StringLeft(), StringRight, StringMid(),
supporting functions StringLen(), StringInString(), and finally the data type
conversion string functions, StringToIntg(),StringToReal(),StringFromIntg,
and StringFromReal().

Example 11 String Replace

StringReplace(Text,SearchFor,ReplaceWith,CaseSens,NumToReplace,M
atchWholeWords)

59
ASCII TABLE

60
Execution Type: Execute
Trigger Type: WhileTrue
Expression: MasterObject.trigger11

1. '##############################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex11 demonstrate String Replace
5. '##############################################
6.
7. dim Message1 as string;
8. dim SearchFor as string;
9. dim ReplaceWith as string;
10. dim CaseSens as integer;
11. dim NumToReplace as integer;
12. dim MatchWholeWord as integer;
13.
14. SearchFor = "Inlet";
15. ReplaceWith = "Outlet";
16. CaseSens = 0;
17. NumToReplace = -1;
18. MatchWholeWord = 1;
19.
20. 'Create a message to the user
21. Message1 = "Inlet Discharge Pressure Input";
22.
23. 'Every occurrence of In is replaced with Out ignoring case but only whole words.
24. LogMessage("Before: " + Message1);
25. Message1 =
StringReplace(Message1,SearchFor,ReplaceWith,CaseSens,NumToReplace,Match
WholeWord);
26. LogMessage("After: " + Message1);
27. LogMessage("");
28.
29. Message1 = "Inlet Discharge Pressure Input";

61
30. SearchFor = "In";
31. ReplaceWith = "Out";
32. CaseSens = 0;
33. NumToReplace = -1;
34. MatchWholeWord = 0;
35.
36.
37. 'Every occurrence of In is replaced with Out ignoring case or whole words.
38. LogMessage("Before: " + Message1);
39. Message1 =
StringReplace(Message1,SearchFor,ReplaceWith,CaseSens,NumToReplace,Match
WholeWord);
40. LogMessage("After: " + Message1);
41. LogMessage("");
42.
43. Message1 = "Inlet Discharge Pressure input";
44. SearchFor = "In";
45. ReplaceWith = "Out";
46. CaseSens = 1;
47. NumToReplace = -1;
48. MatchWholeWord = 0;
49.
50.
51. 'Every occurrence of In is replaced with Out matching case ignoring whole words.
52. LogMessage("Before: " + Message1);
53. Message1 =
StringReplace(Message1,SearchFor,ReplaceWith,CaseSens,NumToReplace,Match
WholeWord);
54. LogMessage("After: " + Message1);
55. LogMessage("");
56.
57. Message1 = "Inlet Discharge Pressure input";
58. SearchFor = "Input";
59. ReplaceWith = "Output";
60. CaseSens = 0;

62
61. NumToReplace = -1;
62. MatchWholeWord = 1;
63.
64.
65. 'Every occurrence of In is replaced with Out where whole word matches.
66. LogMessage("Before: " + Message1);
67. Message1 =
StringReplace(Message1,SearchFor,ReplaceWith,CaseSens,NumToReplace,Match
WholeWord);
68. LogMessage("After: " + Message1);
69. LogMessage("");
70.
71. MasterObject.Trigger11 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example11

Example 11 Code Explanation

Line 1-12
Always provide some basic information about the script. First let’s create
variables that have meaning and are descriptive. Note that we have
named each variable to match it’s purpose in the function and will pass it
into the function in the appropriate order.

Line 14-27
The code in line 14-27 sets up the function by assigning each of the
parameters we want to pass with the correct input data. we could have
just passed the raw data in directly but by using descriptive variables we
are making it easier for someone to follow us that is not familiar with the
script, and believe it or not that someone may be you six months from
now!

In few weeks or months you will completely forget what you were trying to
accomplish. In this snippet we are calling the String Replace function to
63
replace “Inlet” with “Outlet” while ignoring text case and affecting only
whole words.

In the snippet below we are looking at the Log Message function output
in the logger. We can easily see that everywhere the word “Inlet” was
found it was replaced with “Outlet” the partial component “Input” was
ignored because it didn’t match as a whole word.

Line 28-40
In this section of code we are calling the String Replace function to
replace “In” with “Out” while ignoring text case and whole words in other
words anywhere the word ‘in” is found it will be replaced with “out”.

In the snippet from the logger output we see that everywhere the word
“In” was found it was replaced with “Out”.

Line 41-53
In this code we are calling the String Replace function to replace “In” with
“Out” while matching text case ignoring whole words meaning anywhere
the word ‘In” is found with a capital “I” it will be replaced with “Out”.

In the snippet below we are looking at the Log Message function output
in the logger. We can easily see that everywhere the word “In” was found
it was replaced with “Out” the partial text component “in” of the word
“input” was ignored because it didn’t match case.

64
Line 54-68
In this part of the script we are calling the String Replace function to
replace “Input” with “Output” only if there is a whole word match ignoring
case meaning anywhere the word ‘input” or any case variation such as
“Input” is found it will be replaced with “Output”.

Example 12 StringLeft, StringRight, StringMid

StringLeft(Text,Chars)

StringRight(Text,Chars)

StringMid(Text,StartChar,Chars)

In this script I will demonstrate how each of these three string splitting
functions work, these three functions should take care of all of your string
splitting situations.

First, I dimension the variables I will need for the function, in this case I
need a string of text to split, the number of characters left or right of the
end of the string I intend to split and the starting character I am going to
use for a starting point with the mid string function.

In this case I just output the string function to the logger if I wanted to use
the output in an attribute I would just set the attribute equal to the function
call.

65
Execution Type: Execute
Trigger Type: WhileTrue
Expression: MasterObject.trigger12
1. '###################################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex12 demonstrate StringLeft StringRight StringMid
5. '###################################################
6. 'dimension variables
7. dim TextMessage as string;
8. dim Chars as integer;
9. dim StartChar as integer;
10. Chars = 17;
11. StartChar = 10;
12.
13. 'load the variable with a string message
14. TextMessage = "This is a string to test string splitting";
15.
16. 'use a few empty messages just to make some space
17. LogMessage("");
18. LogMessage("");
19. LogMessage("");
20.
21. 'returns the first 10 characters from the left
22. LogMessage(StringLeft(TextMessage,Chars));
23. LogMessage("");
24.
25. 'returns the first 10 characters from the Right
26. LogMessage(StringRight(TextMessage,Chars));
27. LogMessage("");
28.
29. 'returns the first 10 characters after the starting character
30. LogMessage(StringMid(TextMessage,StartChar,Chars));
31. LogMessage("");
32.
33. 'stop script execution
34. MasterObject.Trigger12 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example12

66
Execution Type: Execute
Trigger Type: WhileTrue
Expression: MasterObject.trigger12
1. '###################################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex12 demonstrate StringLeft StringRight StringMid
5. '###################################################
6. 'dimension variables
7. dim TextMessage as string;
8. dim Chars as integer;
9. dim StartChar as integer;
10. Chars = 17;
11. StartChar = 10;
12.
13. 'load the variable with a string message

14. TextMessage = "This is a string to test string splitting";


15.
16. 'use a few empty messages just to make some space
17. LogMessage("");
18. LogMessage("");
19. LogMessage("");
20.
21. 'returns the first 10 characters from the left
22. LogMessage(StringLeft(TextMessage,Chars));
23. LogMessage("");
24.
25. 'returns the first 10 characters from the Right
26. LogMessage(StringRight(TextMessage,Chars));
27. LogMessage("");
28.
29. 'returns the first 10 characters after the starting character

67
30. LogMessage(StringMid(TextMessage,StartChar,Chars));
31. LogMessage("");
32.
33. 'stop script execution
34. MasterObject.Trigger12 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example12

When the script executes it generates three logger messages each


containing the result of the corresponding script function. In logmessage
one we see “This is a string” which is 17 characters from the left
corresponding to the StringLeft function.

The second log message is “string splitting” which are the 17 characters
from the right of the text message corresponding to the StringRight
function.

Finally, the third log message contains “string to test s” 17 characters from
the 10th character form the left corresponding to the StringMid function.

Example 13 StringLen, StringInString

StringLen(Text)

StringInString(Text,SearchFor,StartPos,CaseSens)

Sometimes it’s necessary to know the length of a string of text, the


StringLen function returns an integer value that is the length of the string.
Using the string in the previous example we see its length is 41
characters.

In addition to the length we may need to know if a word or string of text


occurs inside another string. The StringInString function searches for a
specified string of text returning the start position of the first occurring
match.

68
In the script we first declare our variables then populate them with the
appropriate values, in this case we are going to use the same text string
from our last example.

The StartPos integer is created to pass into the StringInString function to


determine where in the string to start our search. We know from the
StringLen function that our string is 41 characters long and the word “test”
begins at the 21st character.

Execution Type:Execute
Trigger Type: WhileTrue
Expression: MasterObject.trigger13

1. '#######################################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex13 demonstrate StringLen and StringInString
5. '#######################################################
6. 'dimension variables
7. dim TextMessage as string;
8. dim Length as integer;
9. dim SearchFor as string;
10. dim StartPos as integer;
11. dim CaseSens as integer;
12. dim Location as integer;
13.
14. 'load the variable with a string message
15. TextMessage = "This is a string to test string splitting";
16. SearchFor = "test";
17. StartPos = 1;
18. CaseSens = 0;
19.
20. Length = StringLen(TextMessage);
21.
22. 'Display the length of the string in the logger
23. LogMessage("");
24. LogMessage("Length of the Text String " + TextMessage + " is " + Length);
69
25.
26. 'Display Starting position of the string in the SearchFor variable
27. Location = StringInString(TextMessage,SearchFor,StartPos,CaseSens);
28. LogMessage("");
29. LogMessage("The string " + "'" + SearchFor + "'" + " begins at character " +
Location );
30.
31. 'stop script execution
32. MasterObject.Trigger13 = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Example13

To see the results of the script run check the logger to see that the length of
our string is 41 characters and the specified string "test" is located
beginning at character 21.

Example 14 StringToIntg StringToReal StringFromIntg


StringFromReal

There will inevitably be times when you will need to translate a number in
a string to an actual number instead of text or need to embed an actual
real time value into a string.

The StringTo and StringFrom functions are used for that purpose. Note
that in the example script I had to enclose the StringToIng function and
the add operator with parentheses to get the desired result within the log
message function. In the sample script that follows we convert a string to
an integer adding 5 to it then convert it back to a string. We do the same
thing with the float.

70
Execution Type: Execute
Trigger Type: WhileTrue
Expression: MasterObject.trigger14

1. '################################################
2. 'Author: Charles Vance
3. 'Date: 08/08/2019
4. 'Script: ex14 demonstrate StringTo and StringFrom
5. '################################################
6. 'Dimension variables
7. dim fltTest1 as double;
8. dim intTest1 as integer;
9. dim fltTest2 as string;
10. dim intTest2 as string;
11. dim TextMessage1 as string;
12. dim TextMessage2 as string;
13.
14. fltTest2 = "3.51";
15. intTest2 = "10";
16.
17. 'convert the string 10 to a number and add 5 to it our result will be 15
18. LogMessage("");
19. LogMessage(intTest2 + " to number add 5 = " + (StringToIntg(intTest2) + 5));
20. 'convert the string 3.51 to a number and add 1.2 to it our result will be 4.71
21. LogMessage("");
22. LogMessage(fltTest2 + " to number add 1.2 = " + (StringToReal(fltTest2) + 1.2));
23. 'convert the string 10 to a number and add 5 to it our result will be 15
24. LogMessage("");
25. intTest1 = StringToIntg(intTest2) + 5;
26. 'convert the value intTest1 to a string using base 10
27. LogMessage(intTest1 + " to string = " + StringFromIntg(intTest1,10));
28. LogMessage("");
29. fltTest1 = StringToReal(fltTest2) + 1.2;
30. 'convert the value fltTest1 to a string with 2 decimals and floating point format
31. LogMessage(fltTest1 + " to string = " + StringFromReal(fltTest1,2,"f"));
32.
33. 'stop script execution
34. MasterObject.Trigger14 = false;

71
After running this script the string “10” is converted to an integer and
added to 5 to get 15, the string “3.51” is converted to a double and
added to 1.2 to get 3.71

Graphic Symbol Scripting

So far we have been writing simple scripts in the object template to


demonstrate the various logic structures having nothing to do with graphics
symbols. When you need operations to be stable and consistent place
them in the object. Graphic symbols may or may not be running all the time
and scripts contained in them will only execute while the graphic is active in
the View Application.

Object scripts on the other hand are active as long as the object is
deployed, always ready to execute when the trigger condition is satisfied. A
good rule of thumb is operations that need to persist are better placed in an
object script rather than a symbol script.

As we saw in the discussion of the BindTo script one object can easily
“bind” or connect with the attributes of another object as long as both
objects are deployed to the same engine. The graphic symbols cannot use
the BindTo function and therefore we use a different means for connecting
symbols to objects at runtime.

The SetCustomProperty function is used whenever we need to make a


connection to a galaxy object from within a graphic symbol. This is a very

72
useful and powerful function for collecting data for display and evaluation
within symbol scripts. A complete discussion of the
SetCustomPropertyValue function is found in Wonderware document
“Creating and Managing Archestra Graphics User’s Guide”

When creating graphic symbols it’s a good idea to already have an object
template in mind, one way to think about it is a hand and glove analogy.
The object in the galaxy is the hand while the Archestra graphic symbol is a
“glove” perfectly fitting the attributes in the object.

The original intent of Archestra was to provide a method for visualization to


model the real world process or machine, matching symbols to objects has
that effect. The following diagram illustrates this principle.

73
When working with symbols there are basically three ways to “marry”
the symbol with an object and its associated matching attributes. We
can embed the graphic symbol into the object, we can assign an object
reference to the symbol using a script reference, or we can use the Set
Custom Property method to assign individual properties to individual
attributes in a symbol script.

Embedding Symbols in objects

The easiest way to align a graphic symbol with object attributes is to


simply embed the graphic in the symbol, this method however is not
without its issues. Embedding symbols in instances is always a bad
idea for several reasons. First, if the symbol only “lives” in an object
instance and multiple copies of that symbol are used to reference other
objects deleting the original instance will destroy the reference to all
other objects, this could be very bad. Imagine the horror of seeing the
symbol in the illustration below in dozens of places all at once.

The best way to avoid this is to NEVER embed a graphic symbol into
an object instance. ALWAYS embed symbols into templates only. This
way as long as the template exists the graphic will remain available for
use and templates cannot be deleted as long as there are child
instances existing under it.

74
Another problem with embedding symbols is the ability to create these
symbols in the template as “stand alone” meaning you can create the
same symbol in multiple places that will need to be modified individually.
Imagine having dozens of different motor or valve symbols scattered all
over the place in multiple templates and now every time you need to
make a simple change you have to locate and touch every single one of
them, this can be a task that takes hours or even days and is totally
unnecessary if a little planning and discipline is used from the
beginning.

I did a video discussing best practices and some simple do’s and don’t
when embedding symbols. You can watch that video at the link below.
https://www.youtube.com/watch?v=5MVVSghKmcY&t=321s.

The main idea is to create a “master” symbol in the graphic toolbox and
then embed that symbol into templates where you need to. Then when
you make changes to the master symbol every template where that
symbol is used will be updated automatically. This one thing will save
you hundreds of hours of programming time.

In tutorial 2 we will use embedded objects as well as demonstrate the


two scripting methods for connecting symbols to objects.

Setting Symbol Owning Object Property

The next method for connecting symbols with objects is to take


advantage of the symbol class Owning Object property. However, since
this property is only exposed when viewed from within another symbol,
two symbols are required.

A base symbol is created containing the animations and properties then


a second symbol is created to contain the base symbol exposing all of
the symbols base properties such as the Owning Object Property and
any custom properties that have been created.

75
The illustration below shows the Base_TestSymbol1 embedded into
TestSymbol1 exposing the Owning Object property.

Once the Base symbol has been placed in the second symbol a string
property is created in the second symbol called “Owner”, this property
will be used in a Data Change script to assign an object reference to
the Owning Object property as seen in the script example.

GitHub Code Link


https://github.com/cbvance/StepByStep/blob/master/OwnerProperty

76
Graphic Info Client

Graphic Info Client is a powerful tool used to create pop-ups or other


remotely referenced symbols. The aaGraphic library is invoked using the
ShowGraphic() function to create or “instantiate” symbol instances at
runtime from a static symbol template.

The static “template” is no different than any other symbol, what makes it
different is the way it’s used. The calling symbol passes information such
as owning object and parameter data to the template symbol through
properties and methods.

A crucial feature of the Graphic Info Client is the ability to create custom
property value pairs and then pass those into the Graphic Info Client for
assignment to custom properties in the template symbol.

In this way we can establish references to Archestra objects in the called


symbol at runtime. If this seems a bit confusing don’t worry it will become
clear when we walk through the Graphic Client tutorial.

The method we will be using to create our pop-up graphics is an action


script animation attached to a button or other graphic that executes the
ShowGraphic() function. As an example I am going to show you a script I
wrote for a project that was Wonderware Intouch only without galaxy
objects. When looking at the script you will see the prefix "intouch:" this
lets Archestra know the reference is in Intouch and not a galaxy object.

The action script I am using in this example was placed on a transparent


rectangle with a dotted red line that was placed around a valve, pump or
other object to indicate a control was present. The figure below is the
control rectangle the action script animation was attached to.


77
The table on the next page is a handy quick reference table containing
commonly used Graphic Info Client properties, for a complete discussion
of all of the properties and methods of the Graphic Client can be found in
the Archestra Application Server Scripting Guide dated 11/17/2015
beginning at page 54, that number may vary between versions.

Graphic info library pop-up creation is demonstrated in the next chapter


in Tutorial 3.

Pop-up Launcher Action Script

1. Intouch:MinSecurityLevel = 999;
2. if Intouch:$AccessLevel > Intouch:MinSecurityLevel then
3.
4. Dim graphicInfo as aaGraphic.GraphicInfo;
5. graphicinfo = new aaGraphic.GraphicInfo;
6. graphicinfo.OwningObject = "4108X_CV1_WireFrame";
7. graphicInfo.Identity = ValveNameName;
8. LogMessage("DeviceName " + ValveName);
9. graphicInfo.GraphicName = "4108X_CV1_Faceplate";
10. graphicInfo.WindowType = aaGraphic.WindowType.Modeless;
11.
12. Dim cpValues [8] as aaGraphic.CustomPropertyValuePair;
13. cpValues[1] = new
aaGraphic.CustomPropertyValuePair("DeviceName", ValveName, true);

78
14. cpValues[2] = new
aaGraphic.CustomPropertyValuePair("OpenCommand", "Intouch:" +
HSOName + ".Name", False);
15. cpValues[3] = new
aaGraphic.CustomPropertyValuePair("CloseCommand", "Intouch:" +
HSCName + ".Name", False);
16. cpValues[4] = new
aaGraphic.CustomPropertyValuePair("AUTOCommand", "Intouch:" +
HSAUTName + ".Name", False);
17. cpValues[5] = new
aaGraphic.CustomPropertyValuePair("MANCommand", "Intouch:" +
HSMANName + ".Name", False);
18. cpValues[6] = new
aaGraphic.CustomPropertyValuePair("CmdName", "Intouch:" +
ValveNameName + ".Name", False);
19. cpValues[7] = new
aaGraphic.CustomPropertyValuePair("Faceplate", FaceplateName, True);
20. cpValues[8] = new
aaGraphic.CustomPropertyValuePair("StopCommand", "Intouch:" +
HSSName + ".Name", False);
21. graphicInfo.CustomProperties = cpValues;
22. graphicInfo.WindowTitle = ValveName;
23.
24. graphicInfo.WindowRelativePosition = 8;
25. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''
26. Dim WinHeight As Integer;
27. Dim WinWidth As Integer;
28. Dim FaceplateHeight As Integer;
29. Dim FaceplateWidth As Integer;
30.

79
31. WinHeight=1050;
32. WinWidth=1680;
33. FaceplateHeight=455;
34. FaceplateWidth=250;
35. if Intouch:$ObjHor > WinWidth-FaceplateWidth then
36. graphicInfo.X =Intouch:$ObjHor+150 - (Intouch:$ObjHor -
(WinWidth-FaceplateWidth));
37. else
38. graphicInfo.X =Intouch:$ObjHor+150;
39. endif;
40.
41. if Intouch:$ObjVer > WinHeight-FaceplateHeight then
42. graphicInfo.Y =Intouch:$ObjVer+150 - (Intouch:$ObjVer -
(WinHeight-FaceplateHeight));
43. else
44. graphicInfo.Y =Intouch:$ObjVer+150;
45. endif;
46. '''''''''''''''''''''''''''''''''''''''''''''''
47. ShowGraphic( graphicInfo );
48. LogMessage("DeviceName " + ValveName);
49.
50. else
51. Intouch:PopUpName = "AccessDenied";
52. endif;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/PopupLauncher

Pop-up Launcher Code Explanation

80
(Author's Note: I did not create a nice explanation for this code
when I first wrote this segment as I have been advising that you
should do, as a result now I will need to analyze this code again so
as I am writing this I am looking at this code again for the first time
in 6 years.)

Line 1 and 2

These two lines set an Intouch tag named "MinSecurityLevel" to a value of


999 to establish a minimum access level for the operation. Line 2 is a
conditional that only allows access if the current user access level is
greater than the security level.

Line 4 - 10

This segment of code creates an instance of the Graphic Info object then
sets the owning object for the pop-up that will be created. SInce this is an
Intouch reference the owner is the calling symbol. We inserted a log
message here to let us know which valve was being initialized.

The GraohicName property tells the pop-up which valve we are controlling
and setting the popup as modeless means we can do other things once
the pop-up appears.

Line 12 - 22

In this segment we dimension the value pair array named “cpValues”


providing the mechanism for us to push values to the new pop-up instance.
In this case we are pushing values from Wonderware Intouch tags by
using the “Intouch:” prefix, but we could just as easily push values from
galaxy object attributes.

After populating each element of the cpValue array with our Custom
Property Value Pairs we push them into the graphicinfo library in line 21. In
81
line 22 we set the title that will display in the header of the popup.

Line 24 - 46

In this segment of code we set the relative position of the control where the
popup appears on the screen. In line 35 and 41 we are checking the
current screen location to see if our popup is going to be displayed off
screen and if it is we change the position to make sure it displays on
screen.

Line 47 loads the graphic and creates the popup.

Line 51 is the else that executes if the user does not have access
displaying another popup with a nice access denied message to the user.

82
83
84
CHAPTER 2
BASIC SCRIPTING
TUTORIALS

85
86
TUTORIAL1: Basic Configuration

There are several ways to display graphic symbols and connect them to
objects in the galaxy. I am not going to attempt to cover every possible
configuration, I want to show you the way I have seen graphics
implemented as a best practice. For example, an Archestra graphic
symbol can be dropped directly onto the Wonderware Intouch screen but
this creates a few problems that make that practice undesireable.

For one thing, if your screen is created in Intouch by dropping individual


graphics onto it, any changes to that screen will always require that you
open the Intouch screen editor, something that can be time consuming.
Secondly, each graphic symbol must have an "owning" object making
proper embedding technique very important.

For these tutorials I will a screen symbol that is embedded into the
Wonderware Intouch window once All other symbols are then "dropped"
into the screen symbol and will automatically appear in the Wonderware
window with the need of opening Window Maker.

All of our screens will be done this way so that our edits are done from
within the Archestra IDE instead of having to open Window Maker every
time we need to make a screen change.

While the purpose of this book isn't to teach graphic programming but it is
necessary to create some graphics before we can see how scripting is
used to control the animations. I'll keep the graphics simple.

In our tutorials we are going to build graphics that provide visualization for
a mixing operation consisting of two fill valves, a mix tank with mixer, a
transfer pump and discharge valve as shown in the figure below.

These tutorials are going to start out very basic and progress in
complexity until by the final tutorial we will have touched all the scripting
87
topics needed for a basic understanding of how they all work together.

Before we get started building our mixing operation screen I'm going to
list and describe some of the most commonly used elements.

The basic elements most commonly used to create screen graphic


animations are the ortho H/V Line, Rectangle, Circle, Button, Text,
Status, Radio Button, Check Box and Combo Box.

The Creating and Managing Archestra Graphics User’s Guide provides a


detailed look at the graphics editor and how to use each of the basic
elements described.

Ortho (H/V) Line


The ortho H/V line element is always either in the vertical or horizontal
plane and cannot be used for diagonal lines. The reason I consider this
to be the most common is because I rarely if ever use the other line
element type is because lines are most frequently used to depict flow
lines or piping in a process and it makes sense to place these process
lines in a vertical or horizontal position like is seen in a process or wiring
diagram, If you use the diagonal line for making straight lines it is easily
moved off center at one end or the other making the overall graphic look
slanted and sloppy. Use the ortho line to ensure all of your straight lines
are clean and neat and easily modified later, having said that sometimes
it’s necessary to have a diagonal line but I try to use it very sparingly.

The animations of a line are going to be minimal, usually visibility,


location,width (horizontal) or height (vertical), color, orientation and
perhaps weight. These tutorials will demonstrate some of the ways a line
can be animated such as color to depict fluid transfer.

Rectangle
Rectangles are used to set graphic backgrounds and are also great for
highlighting data fields and status indicators. In the case of backgrounds
you will discover in the pop-up creation tutorial that if we don’t establish a

88
specific size for the pop-up template Wonderware automatically sizes the
graphic based on the elements you have placed there.

The way we can determine an exact size or background color for our pop-
up is to use a rectangle element with definite size parameters
established.

We will also use the rectangle to demonstrate how to highlight a data field
for input and display, the rectangle is also great for making message
status indicators as the fill color can be manipulated with the data to
create contrasts or highlight the data for proper annunciation or alert
status.

Circle
The circle element can be animated in the same way as a rectangle but
the circle is usually used in a group of elements to display a shape such
as a pump or other production equipment.

Button
The button element is a common and extremely useful element used
often to create a way for an operator to control equipment by making on/
off buttons, mode buttons, pop-up buttons, or many other user controls. In
these tutorials the button element provides a way to start and stop pumps
or open and close valves.

Text
Text elements are probably the most common type of element used when
building graphics, we use the text element as both static labels to identify
equipment and as a dynamic indicators displaying object attribute data.

Status
The status element is a special type of graphic that shows a symbol at
runtime based on the status of the attribute it is connected to. We use
these elements to indicate if a data point has bad quality, is initialized, if
the underlying object is undeployed or connected to its platform, etc.

89
Some software platforms use special symbols such as @@@ to indicate
data quality of connected attributes, since Wonderware Intouch doesn’t
include this feature we use the Status element instead.

Radio Button
The radio button element is a selector used whenever we need to create
multiple options and only one option at a time is available. In our tutorial
the radio button is used to make line or tank fill color changes depending
on which radio button is selected. We can add as many radio buttons to
the button group as are needed and it can reference an object attribute if
necessary.

Checkbox
The checkbox element is used whenever we need to create options that
have nothing to do with any other option, this is a good way to allow the
user to set a boolean attribute state to true or false. In our example we
are using the checkbox to make enable or disable the two tank level
alarms.

Combobox
The combo box element allows us to provide means for the operator to
pick values from a drop down list and then use the selected value to
write to another attribute.

Now we are ready to get started building the mixing screen in tutorial 1.

Step 1 : Create the Screen Layout Symbol and Embed into


Wonderware Intouch Window.

In the tutorial galaxy I will first create a new Graphic Toolset named
Tutorial1 then create a symbol called T1MixerScreen in that toolset as
seen in the figures on the next page. Then we will create a window in the
view application called Tutorial1 with our screen graphic embedded.

90
Once the T1MixerSymbol is created we can create a new View
Application called Tutorials with a new window called
T1MixerScreen. In the T1MixerScreen figure below notice that the
Width is 1024 and the Height is 768 and the FillColor property is set
to gray. In the next figure we embed the T1MixerGraphic into teh
window we named T1MixerScreen.

91
92
Step 2: Create the Graphic Symbols for the Tank, Pump, Mixer
and Valve.

In step 2 of tutorial 1 we are going to create the basic symbols needed to


represent a simple filling, mixing and transfer operation.

Tank Graphic
The first symbol created is the tank symbol. We are going to use 3
elements to make the tank, the rectangle and two Chords for curved top
and bottom and a second rectangle for level indication. two text elements
are added to display tank name and the level float value.

Add a float custom property named Level to the symbol and attach it to
the second rectangle using the %FillVertical animation. The custom
property is given the default value of "me.Level.PV" allowing us to use the
contained name "Tank101.Level.PV" when the me dot reference is
replaced with the object name at runtime. Next add a Text element with
default value "me.Tagname" attaching the Tagname Custom Property to
tank name display text. Finally attach the text "#.#" to an analog value
display animation.

93
Create the Custom Properties

Create the Float Value Animation

Attach the % Fill Vertical animation to the second rectangle

94
Pump Graphic
The next symbol created is the pump symbol. We are going to use 3
elements to make the pump, an ellipse, rectangle and polygon. Each
element of the pump is animated using the Fill Style animation connected
to the Running custom property.

The status of the pump and its object name will be displayed using text
elements. The text element displaying the tagname is a string value
display animation while the status animation is controlled using a Data
Change script.

Add a discrete custom property named running to the symbol and attach
it to the three pump elements using the Fill Style animation. The custom
property is given the default value of "me.Run" allowing it to match the
run attribute in the motor object. Then add a Text element with default
value "me.Tagname and attach the Tagname Custom property to display
tank name.

95
Add Fill Style animation to the pump body elements

Add Script to update pump status

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/MotorTutorialStatus

96
Valve Graphic
The next symbol created is the valve symbol. We are going to use 2
elements to make the valve, two polygons. Each element of the valve is
animated using the Fill Style animation and an integer truth table.

The status of the valve and its object name will be displayed using text
elements. The text element displaying the tagname is a string value
display animation while the status animation is controlled using a Data
Change script.

Add three discrete custom property named status update, OpenSW and
CloseSW and add an integer property named status to the symbol then
attach the status integer to the valve polygon elements using the Fill
Style animation. The two discrete custom properties are given default
values matching the object UDA name allowing them to match attributes
in the valve object. Then add a Text element with default value
"me.Tagname and attach the Tagname Custom property to display tank
name and another text element named "status".

97
The motorized valve in the tutorial operates based on the truth table at
the top of the page. The truth table is converted to an integer value
and then the fill animation determines color.

Status 1 = Valve Open Green


Status 2 = Valve Closed Dark Gray
Status 3 = Valve in Travel White
Status 4 = Valve in Trouble Magenta

98
Create Custom Properties
(Make sure Tagname string is set as tag reference)

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/ValveScript

99
Mixer Graphic
The next symbol created is the mixer symbol. We are going to use 3
elements to make the mixer, a round rectangle for the mixer motor a H/V
line for the shaft and a polygon as the mixer blades. Each element of the
valve is animated using the Fill Style animation referencing the Boolean
run property

The mixer object name will be displayed using text element. The text
element displaying the tagname is a string value display animation set as
a tag reference

Add three discrete custom property named status update, OpenSW and
CloseSW and add an integer property named status to the symbol then
attach the status integer to the valve polygon elements using the Fill
Style animation. The two discrete custom properties are given default
values matching the object UDA name allowing them to match attributes
in the valve object. Then add a Text element with default value
"me.Tagname and attach the Tagname Custom property to display tank
name and another text element named "status".

100
Create the Custom Properties

Create the Animation

101
Step 3: Create Object Templates

Next we are going to create the object templates for the pump, valve
and mixer. Each object template contains the information needed to
represent a particular type of equipment. All of the templates are
derived from the $UserDefined template.

In the Field Attribute Tab Create the following attributes: (If using
later versions of Archestra you may not have field attribute tab, in this
case create a standard attribute with IO enabled)

1. Valve Template Name -$Motor


Name | BearingTempHi | Attribute Type | Discrete | Access Mode |
Input | Description | “Bearing Temp Hi” | Input Source |
me.udaBearingTempHi | Enable state labels | True | False State |
NORMAL | True State | ALARM | Enable state alarm | True | Active
Alarm State | ALARM | Priority | 1

Name | Run | Attribute Type | Discrete | Access Mode | Input |


Description | “Running” | Input Source | me.udaRun

Name | Start | Attribute Type | Discrete | Access Mode | InputOutput |


Description | “Start” | Input Source | ---

Name | Stop | Attribute Type | Discrete | Access Mode | InputOutput |


Description | “Stop” | Input Source | ---

102
103
2. Valve Template Name -$MotorizedValve
Name | CloseCmd | Attribute Type | Discrete | Access Mode | InputOutput
| Description | “Valve Close Command” | Input Source | ---

Name | CloseSW | Attribute Type | Discrete | Access Mode | Input |


Description | “Valve Close Limit Switch” | Input Source | me.udaCloseSW

Name | OpenCmd | Attribute Type | Discrete | Access Mode | InputOutput


| Description | “Valve Open Command” | Input Source | ---

Name | OpenSW | Attribute Type | Discrete | Access Mode | Input |


Description | “Valve Open Limit Switch” | Input Source | me.udaOpenSW

Name | StopCmd | Attribute Type | Discrete | Access Mode | InputOutput |


Description | “Valve Stop Command” | Input Source | ---
3. Valve Template Name -$Level
Name | PV | Attribute Type | Float | Access Mode | Input | Description |
“Tank Level” | Input Source | Object1.udafloatTest

4. Valve Template Name -$Tank


The Tank template has no attributes it is a container only.

Once we have created all of the graphic symbols and object templates for
tutorial 1 we are ready to create object instances and deploy. I am going
to show you how to use object contained names to reference in the
graphics, this is a best practice since it insulates you from object name
changes. The figure below shows the containment structure for this
tutorial. When using containment to access the tanke level we will use
Tank101.Level, Tank101.MX101, Tank101.MOV101, etc.

104
105
Step 5: Create the Mix Tank Screen Graphic

Build out the mix tank screen graphic using the individual symbols
created in previous steps. Open the MixTankScreen graphic and
select and drop the tank, three valves a mixer and pump graphic.

Draw lines representing product flow based on the mixing system


diagram on page 78.

The figure on page 98 represents the mixing system graphic with the
symbols placed and lines drawn. Notice that I have renamed the
graphic symbols to match its name in the galaxy. Notice that the
"Owning Object" property is empty, this will need to have the correct
object name entered to make the symbol reference the correct
object.

If we embedded the appropriate symbol into the object template all


we would need to do is make sure the correct embedded graphic
was used to drop the symbol. This tutorial doesn't use embedded
graphics thus we need to make the references manually.

Use the following object references in the Owning Object property for
each symbol.

Tank symbol Owning Object = Tank101


Mixer symbol Owning Object = Mixer101
MOV101 symbol Owning Object = MOV101
MOV102 symbol Owning Object = MOV102
MOV103 symbol Owning Object = MOV103
Pump symbol Owning Object = Pump101

106
107
Step 6 : Test the Mixing Screen Graphic with all its symbols and
objects.

In the next tutorial we are going to introduce the open source Modbus
simulator for testing tags but for now we are going to use the object
viewer.

In the object viewer setup the following tags.

Experiment with setting the MOV valves to various conditions making


sure the valve color follows the truth table from page 80.

Remember we put UDA attributes in the input source for this purpose,
when we use the Modbus simulator we will create a DI object and place a
reference to the assigned Modbus address.

I have set MOV101 as closed, MOV102 as open, MOV103 and travel and
the Mixer running. Pump101 is stopped and the tank is 50% filled. The
graphic on the next page shows this tutorial graphic at runtime.

Tutorial 1 Complete

108
109
TUTORIAL 2: Simulation and Testing

A very important aspect of programming real time systems is to test your


code. There are a couple of schools of thought on testing code, one is to
design everything the nth degree up front without much if any testing as
you write the code, the other school of thought is to do incremental
testing as you build your code.

I personally subscribe to the second method, I like to test my code as I


am writing it so I know it works. Perhaps in the early days of main-frame
computers when computer time was expensive and time consuming it
made sense to have your code as correct as possible before loading it
before attempting a trail run but today with instantaneous in line
troubleshooting and diagnostics available at our fingertips it makes a lot
more sense to test as much as possible.

The next decision we face is how do we test? Obviously we are


programming and HMI that is connecting to a PLC or RTU we would like
to have the actual device connected in real time to test so we are sure we
have it right, unfortunately due to costs of hardware this option is usually
not available in the early stages of code build and we are left to find other
methods.

As you have already seen the easiest way to test Archestra code is to use
the object viewer with internal UDAs placed in the input source or out[ut
destination property of the attribute we intend to aim at the field device.
Yet, while this is a decent method for quick testing it is very limited and
we are not able to do much scenario testing such as triggering other
objects or scripts to test interaction of multiple objects or operations. The
object viewer test method is simply a noe at a time manual means to turn
things on and off or set values but isn’t very useful for scenario testing.

Fortunately there is a very good open source solution for testing that I
have used with success for testing that I will show you here as Tutorial 2.

110
Step 1: Download Modbus Simulator
Browse to www.plcsimulator.org and download the program from the
Downloads page. Follow the installation instructions then follow the
register/unlock directions to turn off the nagware screen that pops up
after 45 minutes. This simple modbus simulator is very easy to use and
has some very powerful features for simple testing such as the ability to
load a VBS script to run simulation code for scenario testing.

You can use any modbus OPC server to connect the simulator to the
galaxy I choose to install and use the Archestra DASMBTCP but
Kepware has a demo version of its modbus data server available on its
website https://www.kepware.com/en-us/products/kepserverex/drivers/
modbus-ethernet/

that will run 2 hours before stopping which should be plenty of time for
basic code testing if you choose that route.

111
Step 2: Configure Data Server Software

For this tutorial I will show you how to configure the Archestra
DASMBTCP if you choose to use the free Kepware modbus server follow
the directions provided by that vendor to setup the DAS.

I am assuming that if you have access to the Archestra galaxy software


the DASMBTCP is also available on the device integration downloads
from Wonderwaere so I’m not going to go into detail about how to install
the DAS. If you have any issues getting the Wonderware DASMBTCP
use the free Kepware modbus server.

Once the DASMBTCP is installed on your HMI use the SMC to configure
it.Create a new TCP-IP Port object notice that the default port is 502, this
is the standard modbus port.

Next Add the ModbusPLC object and set it up as seen in the illustration
on the next page.Make sure you put the name or IP address of your
computer in the Network Address box, my computer name is Charles-PC
yours may differ.

Next set the Device Group name and poll interval. I have named mine
PLC1 with a 1000 millisecond update rate. You can name this anything
you want but write it down somewhere as it will be needed in the next
step when we setup our DI object in the galaxy.

Once you are satisfied the DAS is setup correctly right click the
Archestra.DASMBTCP.3 icon and select Configure as Service option
making sure Auto Service is selected this will ensure the DAS starts on a
reboot of the computer. After configuring the DAS as a service select
Activate Server and the red x on the icon is replaced with a green check
mark.

112
113
Step 3: Configure galaxy DI Object

The object we are going to use to connect to the DAS is the


DDESuiteLink client object.. Start by creating one of these as shown in
my galaxy view below.

I have deployed this object on a remote platform because that is where I


installed the DAS, make sure this object is deployed where your DAS is
installed. Make sure the Topic name you configure matches exactly with
the DEvice group name in the DAS.

When you have completed the configuration you can deploy the SuiteLInk
DI object and check to see if it is connected to the DAS. By default the
connection status will be “Disconnected” in the illustration provided you
will see the ConnectionStatus and Reconnect attributes of the DI Object.
To connect we can manually set the Reconnect attribute to true.

114
The DI Object doesn’t automatically attempt to reconnect to the DAS
when the connection is lost therefore we are going to add a script to the
DI Object to force a reconnect anytime the connection is lost. The
illustration below is the Data Change Script you will need to put in the
SuiteLink Client object for auto reconnect.

Step 4: Establish IO for testing the tutorial

In this step we are going to establish an IO table for the tutorial and apply
those modbus addresses to the appropriate input source or output
destination.

Assign the input source of each object attribute to its corresponding IO


assignment as indicated in the IO Table on the next page the illustration
below the table shows how I did the run status for Mixer 101, follow the
same pattern for all of the object attributes.

115
116
Step 5: Test the IO points using object viewer.

Open object viewer for each object and examine each of the attributes
along with its input source and input read status. The read status should
be empty and quality should be good for all items. If not verify that the
DAS is active and running, that the SuiteLink Client object is deployed
and connected to the DAS. Finally, verify that the attribute input source is
referencing the SuiteLink Client and Topic name path correctly. The
syntax is <DIObject.TopicName.ModbusAddress>

When the object attributes are confirmed with good quality you are ready
to manipulate the Modbus simulator to change the state of the attribute
data.

Simulator Discrete IO
Inputs

In the simulator I/O drop down select Digital Inputs option and notice that
the register map shows all digital inputs. Looking in the upper left corner
of the matrix we see the address 10001 - 10016 disregard the +15 and
note that the addressing begins at 1 on the left all the way to 16 on the
right. Click the bit location on the matrix to set the bit to a logical one or
zero. If dark its a logical one, otherwise its a logical zero. Set the 4th bit
from the left to turn on 10004 which corresponds to the Pump 101 run
status confirming the run attribute indicates as true in the object viewer.

Outputs

To confirm outputs are working select the Coil Outputs option from the simulator I/O
drop down and observe the matrix change to show all output addressing. In a similar
fashion as the inputs all outputs start at 1 from left to right. To confirm go into the
object viewer and set the Pump 101 Start Command to true. Pump 101 Start
Command sets 00005 the 5th bit from the left should now be blacked out in the
matrix view.

117
Discrete Inputs

Discrete Outputs

118
Simulator Analog IO

We are using the simulator holding registers for our analog values. Notice
that even though our attribute is a float we are connected to a single
register and treating it as if it were an integer, the reason is because
Modbus requires two registers to represent a float and the simulator does
not have a float command in the VBS engine I am trying to keep it simple
and manually setting a float value using IEEE format is painful and
honestly not necessary at this point. In a later section of the book dealing
with Custom scripts I include instructions on how to create a DLL that can
be loaded into the simulator VBS to provide full DASMBTCP data type
support including floats and bitwise operator, for now though the
simulator integer will work fine with the Archestra float attribute.

From the I/O drop down select Holding Registers and the register matrix
map will change to show all of the Holding registers in the simulator.
NOtice that the address numbering begins with 40001 at the upper left
and goes up to the right. Confirm Tank Level 101 by setting a value in the
40002 register position confirming the same value appears in the Level
101.PV attribute in object viewer.

Analog IO

119
Step 6: Prepare the Simulator VBS script for scenario testing.

The modbus simulator allows you to load a VBS program into the
simulator to add a bit of simulation control to the valves and pumps. I the
script I have written you can turn pumps off or and open and close valves.
Feel free to experiment adding additional simulation scenarios.

To load the VBS script you must click on the small man icon and then
manually type in the path and filename of your VBS script. Please note
that the browse feature is not functional and will crash the simulator.

Load the VBS script and use the object viewer to set the command bits to
true observing the corresponding switch inputs automatically set to true in
response to the action of the simulator VBScript.

1.
2. ctr = ctr + 1
3. SetRegisterValue 3,0,ctr
4. if ctr = 100 then
5. ctr = 0
6. end if
7.
8.
9. 'MOV101 Control
10.
11. 'Get the OpenCmd address - 1
12. Open = GetRegisterValue(0,06)
13. If Open = 1 then
14. SetRegisterValue 1,05,1
15. SetRegisterValue 1,06,0
16. SetRegisterValue 0,06,0
17. End if

120
18.
19.
20. 'Get the CloseCmd address - 1
21. Close = GetRegisterValue(0,07)
22. If Close = 1 then
23. SetRegisterValue 1,05,0
24. SetRegisterValue 1,06,1
25. SetRegisterValue 0,07,0
26. End if
27.
28.
29. 'MOV102 Control
30.
31. 'Get the OpenCmd address - 1
32. Open = GetRegisterValue(0,09)
33. If Open = 1 then
34. SetRegisterValue 1,07,1
35. SetRegisterValue 1,08,0
36. SetRegisterValue 0,09,0
37. End if
38.
39.
40. 'Get the CloseCmd address - 1
41. Close = GetRegisterValue(0,10)
42. If Close = 1 then
43. SetRegisterValue 1,07,0
44. SetRegisterValue 1,08,1
45. SetRegisterValue 0,10,0
46. End if
47.

121
48. 'MOV103 Control
49.
50. 'Get the OpenCmd address - 1
51. Open = GetRegisterValue(0,12)
52. If Open = 1 then
53. SetRegisterValue 1,09,1
54. SetRegisterValue 1,10,0
55. SetRegisterValue 0,12,0
56. End if
57.
58.
59. 'Get the CloseCmd address - 1
60. Close = GetRegisterValue(0,13)
61. If Close = 1 then
62. SetRegisterValue 1,09,0
63. SetRegisterValue 1,10,1
64. SetRegisterValue 0,13,0
65. End if
66.
67. 'PUMP101 Control
68. Start = GetRegisterValue(0,4)
69. If Start = 1 then
70. SetRegisterValue 1,03,1
71. SetRegisterValue 0,4,0
72. End If
73.
74. Stopped = GetRegisterValue(0,5)
75. If Stopped = 1 then
76. SetRegisterValue 1,03,0
77. SetRegisterValue 0,5,0

122
78. End If
79.
80. 'MIXER101 Control
81. Start = GetRegisterValue(0,2)
82. If Start = 1 then
83. SetRegisterValue 1,02,1
84. SetRegisterValue 0,2,0
85. End If
86.
87. Stopped = GetRegisterValue(0,3)
88. If Stopped = 1 then
89. SetRegisterValue 1,02,0
90. SetRegisterValue 0,3,0
91. End If

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/VBSSimScript

Explanation of VBS Simulation code

Line 2-6

The VBS code executes in a looping fashion much like old style VB
programs did. When writing lines of code for the VBS simulator
keeping that in mind is very helpful. The first few lines of the
simulator script create a free running counter that resets every time it
reaches a count of 100.

In line 2 the value is placed in the modbus register using the


SetRegisterValue() function. The value is placed in modbus register
"0" which corresponds to 40000 in the modbus sim.

123
This is useful as a means to verify that the simulator code is
scanning, if you make a mistake in the code scanning stops and this
number will not update.

Line 9 through 26

This section of code contains two if structures, one for controlling the
response to an open command and the other for the close command.

Line 12 retrieves the value from the modbus register assigned to


MOV101 open command (see the IO table on page 116) and if it is a
logical one executes the set register functions. It is imp[ortnat to note
that the modbus simulator uses zero based addressing thus our
addresses are off by 1. For example, GetRegisterValue(0,06)
corresponds with modbus output register 000007.

Line 14 sets the MOV101 Open Switch modbus address 100006


using the SetRegisterValue( ) function to true and line 15 sets the
Close Switch false with line 16 setting the command back to zero.

This is repeated for MOV101 Close Command in lines 20-26.

Lines 29- 65 repeat the above functions for the other two valves
MOV102 and MOV103.

The remaining VBS code lines 67-91 do the exact same thing for the
pump and mixer control.

Step 7: Load Simulator VBS code for testing.

Loading the VBS script is a simple matter of saving teh script file with
a .vbs extension in a location eaily browsed, I try to place the VBS
script file in the same directory as the simulator executable.

124
Running the simulator is also very easy, browse to the executable
and either double click it or right click and select run as
administrator.

The illustration below is what you will see when the simulator first
runs.

In the upper right corner of the simulator is a tool bar, the 5th icon
from the left is the option select to load a custom VBS script as seen
below.

125
Selecting the VBS auto script icon will cause the dialog below to
appear.

It is important to note that the open source simulator has a bug in


this function such that if you attempt to click the drop down and
browse for the VBS file the simulator will crash. This is a known issue
but since the product is no longer being updated we must manually
entire the path and filename into the script selection box. Select the
Training PLC Simulation radio button and enter the file path and
name to your VBS script.

TUTORIAL 2 COMPLETE

126
TUTORIAL 3: Scripting for Graphic Pop-Ups and
Using Wizards
In Tutorial 3 we are going to expand on the symbols we created in
Tutorial 1 for the mixer screen by adding a popup launcher to launch
a popup for commanding the valves, mixer and pump to operate. We
will be using graphic info library and the Custom Value Pair property
to pass command or status values to the popup.

Step 1: Add Command Popup Launcher to the Motorized Valve


Symbol
Click the magic wand icon to open the wizard options dialog then
add new choice group "ValveType" two new choices, Status and
Commanded. This will create two new layers that we can use to
segregate the new command property and popup launcher. With
these two layers created we will now see two choices when dropping
the symbol onto the Tank Mixer Screen graphic.

127
After you create the two new choices under the ValveType choice group
move all of the existing elements, named scripts, and attributes into both
layers as seen below.

Next, create the popup launcher rectangle that will drop on top of the
valve symbol when the commanded choice is selected. The popup
rectangle in this example is 75 pixels by 95 pixels but this can be anysize
desired just make sure you cover teh valve object and keep it tightly
contained to the size of the valve. Name the rectangle "PopupLauncher"
then change the fill to transparent with a red dotted outline as shown
below.

128
Move the popup launcher rectangle into the commanded choice layer
element area as shown below and then attach the action script animation
provided here.

Popup Launcher Action Script

1. Dim graphicInfo as aaGraphic.GraphicInfo;


2. graphicinfo = new aaGraphic.GraphicInfo;
3. graphicinfo.OwningObject = Me.Tagname;
4. graphicInfo.GraphicName = "CommandPopup";
5. graphicInfo.WindowType = aaGraphic.WindowType.Modeless;
6.
7.
8. Dim cpValues [8] as aaGraphic.CustomPropertyValuePair;
9. cpValues[2] = new aaGraphic.CustomPropertyValuePair("OpenCommand",
Me.Tagname + ".OpenCmd", false);
10. cpValues[3] = new aaGraphic.CustomPropertyValuePair("CloseCommand",
Me.Tagname + ".CloseCmd", false);
11.
12. graphicInfo.CustomProperties = cpValues;
13. graphicInfo.WindowTitle = Me.Tagname;
14.
15. graphicInfo.WindowRelativePosition = 8;
16. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''

129
17. Dim WinHeight As Integer;
18. Dim WinWidth As Integer;
19. Dim FaceplateHeight As Integer;
20. Dim FaceplateWidth As Integer;
21.
22. WinHeight=768;
23. WinWidth=1024;
24. 'FaceplateHeight=455;
25. 'FaceplateWidth=250;
26. if Intouch:$ObjHor > WinWidth-FaceplateWidth then
27. graphicInfo.X =Intouch:$ObjHor+150 - (Intouch:$ObjHor -(WinWidth-
FaceplateWidth));
28. else
29. graphicInfo.X =Intouch:$ObjHor+150;
30. endif;
31.
32. if Intouch:$ObjVer > WinHeight-FaceplateHeight then
33. graphicInfo.Y =Intouch:$ObjVer+150 - (Intouch:$ObjVer -(WinHeight-
FaceplateHeight));
34. else
35. graphicInfo.Y =Intouch:$ObjVer+150;
36. endif;
37. '''''''''''''''''''''''''''''''''''''''''''''''
38. ShowGraphic( graphicInfo );
39.

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/Tutorial3Launcher

130
Step 2: Create Motorized Valve Popup Symbol

The motorized valve command popup consists of a base rectangle 225


pixels x 175 pixels to establish the size of the popup, an open command
pushbutton, a close command pushbutton and the valve symbol graphic
to show status. We use the same graphic symbol as the calling graphic
makingsure the "Status" ValveType is selected otherwise we will see the
red outline of the launcher.

It isn't necessary to assign an object owner in the valve object owning


property as thus will be assigned by the calling graphic. Create two string
properties "OpenCommand" and "CloseCommand" making sure they are
public properties and are text only.

On the next page I have provided illustrations of the valve graphic


configured to indicate status on the command popup and the command
popup custom property dialog showing that the custom properties are
public and text only.

131
132
Step 3: Add Command Popup Launcher to the Pump and Mixer
Symbol

Repeat the same steps with the motor symbol and mixer symbol that we
just did for the valve symbol, add the popup launcher rectangle making it
the correct size to cover the pump element then make it transparent
setting the outline to red dotted line and attach the same script we used
for valve except change line 4 graphic name from "CommandPopup" to
"CommandPopup_Motor" for the pump symbol and
"CommandPopup_Mixer" for the mixer symbol and lines 11 and 12 to the
following

cpValues[2] = new
aaGraphic.CustomPropertyValuePair("StartCommand",
Me.Tagname + ".Start", false);

cpValues[3] = new
aaGraphic.CustomPropertyValuePair("StartCommand",
Me.Tagname + ".Stop", false);

Step 4: Create Command Popup Symbol for Pump and Mixer

Duplicate the CommandPopup symbol twice then rename the copies fto
CommandPopup_Motor and CommandPopup_Mixer. The custom
properties will be changed from "OpenCommand" and "CloseCommand"
to "StartCommand" and "StopCommand" respectively. drop the matching
motor and mixer graphic making sure its Type is configured as "Status".

The two illustrations on the next page show what these two command
popups should look like.

133
134
TUTORIAL 3 COMPLETE

You should be able to view a screen like mine and click on each symbol
to get a popup that sends a command to the object and get feedback
from the simulator.

135
TUTORIAL 4: Using Dot Net Framework Data Types in
Native Scripts

While there remains a lot of overhead code when using dot net type
functions in Archestra, and the native scripting language has certain
limitations, in most cases we are still able to create functional scripts
directly accessing the power of dot net. In the next Section of this book I
will show you how to escape the limitations of the Archestra native
scripting language by using the C# language to create our own Dynamic
Link Libraries importing them as Custom Scripts. For now though I will
use the Archestra scripting language and the Types feature to create a
few dot net functions.

In order to leverage the power of dot net to access information and


functions only available in the dot net framework it is necessary to
provision the external library from “Types” adding the correct namespace
and class before creating an object instance from that class using a
process known as “instantiation”.

When we create a template in Archestra this is very similar to creating a


class structure. Before a template we create in Archestra can be used we
have to create an object instance of that template. This is a very visual
process and we see the little blue ball with a yellow deployment box as
evidence that an instance was created.

When creating object instances in our scripting the same thing occurs but
there is no visual representation of it. There are many libraries called
“namespaces” available for use in dot net framework and we will only
address a few of them here but the process of exposing and using the
namespace libraries is the same.

136
The “Types” feature is accessed via the f(x) option in the upper right
corner of the script editor. Clicking the f(x) icon accesses the Script
Function Browser dialog where we can see many shortcut accessors to
the other functions available in Archestra scripting. For this tutorial we will
be using the “Types” feature which opens all of the dot net framework
types to us.

The illustrations below show what appears when we click the script
function browser icon f(x) and then what the Types subfolder contains. In
our first example we are going to access system variables to discover
information about the user and the computer the object is deployed to.

137
Example 1 System Namespace Environment Class

The dot net environment class contains some very useful information
about the operating system and target computer sometimes it may be
useful to know which user is signed on to a particular deployed platform
or perhaps some other information such as the current working directory
or which domain the logged in user is operating from.

The table below describes the information available to the dot net
environment class.

Step 1: Create the Environment Type Object Template

For this example we are going to create a $UserDefined object template


called $EnvironmentType deployed to the ExmplesArea on the Galaxy
Repository node application engine.

Step 2: Create the UDAs needed for the Environment Class Type

The UDAs required are:

Name | CurrentDirectory | Data Type | String | Category | User writeable

Name | DomainName | Data Type | String | Category | User writeable

138
Name | Drives | Data Type | String Array 10 elements | Category | User
writeable

Name | Initialize | Data Type | Boolean| Category | User writeable | Value


| True

Name | MachineName | Data Type | String | Category | User writeable

Name | SystemDirectory | Data Type | String | Category | User writeable

Name | UserName | Data Type | String | Category | User writeable

An illustration of the template UDA page is illustrated below.

139
Step 3 : Create the Environment Type Script

This script goes in the template and is called "Environment" it is well


documented and self describing.

The illustration below shows the script execution configuration.


me.Initialize is set true as default and then set to false on script execution

1. '#############################################################
2. '
3. ' Script Environment - A script to demonstrate accessing the
4. ' Dot Net Environment Namespace to extract
5. ' information about the OS and target computer
6. '
7. '#############################################################
8. dim i as integer;
9.
10. 'give the object time to stabilize before contacting dot net
11. if Me.Environment.ExecutionCnt >= 2 then
12. 'provision the correct class variable from dot net
13. dim MachineName as string;
14. dim UserName as string;
15. dim DomainName as string;
16. dim CurrentDirectory as string;
17. dim SystemDirectory as string;
18. dim drives[10] as string;
19.
20. 'Use a try catch statement in case something goes wrong

140
21. try
22. 'provision the correct class variable from dot net
23. MachineName = System.Environment.MachineName;
24. UserName = System.Environment.UserName;
25. DomainName = System.Environment.UserDomainName;
26. CurrentDirectory = System.Environment.CurrentDirectory;
27. SystemDirectory = System.Environment.SystemDirectory;
28. drives = System.Environment.GetLogicalDrives();
29.
30. 'assign the data retreived form the class varibales into object UDAs
31. Me.MachineName = MachineName;
32. Me.UserName = UserName;
33. Me.DomainName = DomainName;
34. Me.CurrentDirectory = CurrentDirectory;
35. Me.SystemDirectory = SystemDirectory;
36.
37. 'iterate through each array elemnt to load the array UDA
38. for i = 1 to 9
39. LogMessage(drives[i]);
40. Me.Drives[i] = drives[i];
41. next;
42.
43. catch
44. 'if there was a problem stop script execution
45. Me.initialize = false;
46. endtry;
47.
48. 'stop script execution
49. Me.initialize = false;
50.
51. endif;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/EnvironmentTypes

141
Step 4: Deploy and Test Object Instance

Once the template is created we can create anew instance by right


clicking the template and selecting New/Instance. The new instance will
be in the unassigned Host folder in the deployment view. Move the new
object instance into the ExamplesArea and deploy as shown below.

Testing to determine if the script is working correctly is easily down with the Object
Viewer. The illustration on the next page is the object viewer with all of the UDAs in
the watch window. Note that items in the right hand pane are automatically added to
the watch list and only show values as the appeared when the object was first
initialized. You will need to select each item you need to view and either drag it into
the watch list or right click the item and select Add to Watch.

142
143
Example 2 System Namespace Process Class

The dot net process class provides a means to discover all of the running
processes and the process ID of each one. In this example we will load
the processes and IDs into an array UDA then use that information to
load a list box with process name along side its process ID.

Step 1: Create the $SystemProcesses Template

For this example we are going to create a $UserDefined object template


called $SystemProcesses deployed to the ExmplesArea on the Galaxy
Repository node application engine.

Step 2: Create the UDAs needed for the ProcessClass Type

The UDAs required are:

Name | killID | Data Type | Integer | Category | User writeable

Name | killProc | Data Type | Boolean | Category | User writeable

Name | runGetProc | Data Type | Boolean | Category | User writeable

Name | ProcessNames | Data Type | String Array 300 elements |


Category | User writeable

Name | ProcessIDs | Data Type | String Array 300 elements | Category |


User writeable

Name | ProcName | Data Type | String | Category | User writeable

144
Step 3 : Create the GetProcs Script

This script goes in the template and is called "GetProcs" it is well


documented and self describing.

The illustration below shows the script execution configuration.


me.Initialize is set true as default and then set to false on script execution

1. '###################################################
2. '
3. ' Script GetProcs - A script to demonstrate retrieval pf process
4. ' names and Ids
5. '
6. '###################################################
7.
8. 'let the object stabalize
9. if Me.GetProcs.ExecutionCnt > 3 then
10. dim i as integer;
11.
12. 'provision the Diagnostic.Pricess class
13. dim proclist[301] as System.Diagnostics.Process;
14. 'load the process object array with the processes
15. proclist = System.Diagnostics.Process.GetProcesses();
16.
17. 'the number of processes is unknown so using try catch
18. 'prevents errors from appearing in the log if we overrun the array

145
19. try
20.
21. 'iterate though the arrays to load the object UDAs
22. for i = 1 to 299
23. LogMessage(i + " " + proclist[i].ProcessName);
24. Me.ProcessNames[i] = proclist[i].ProcessName;
25. Me.ProcessIDs[i] = proclist[i].Id;
26. next;
27. catch
28. Me.runGetProcs = false;
29. endtry;
30.
31. 'stop script execution
32. Me.runGetProcs = false;
33. endif;
34.

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/GetProcsScript

Step 4 : Create the KillProc Script

This script goes in the template and is called "KillProc" it is well


documented and self describing.

The illustration below shows the script execution configuration.


me.Initialize is set true as default and then set to false on script execution

146
1. '#####################################################
2. '
3. ' Script KillProcs - A script to demonstrate shutting
4. ' process down
5. '
6. '######################################################
7. 'provision the Diagnostic.Pricess class
8. dim proc as System.Diagnostics.Process;
9. 'find the correct proicess by ID number
10. LogMessage(Me.killID);
11.
12. 'always use a try catch when working with dot net types
13. try
14. proc = System.Diagnostics.Process.GetProcessById(Me.killID);
15.
16. LogMessage(proc.ProcessName + " " + proc.Id);
17.
18. 'kill the process
19. proc.Kill();
20. catch
21. endtry;
22.
23. Me.killProc = false;

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/KillProc

147
Step 5 : Create the Process List Graphic

The Process List graphic is a symbol containing a drop down box, a


couple of buttons and two text references for display of the Process
Name and the Proces ID.

The graphic consists of two custom properties, ProcessID an integer and


ProcessName a String. There are also scripts for loading the combo box
with the contents of the array we loaded the process data into.

The illustration below shows the Process List graphic editor and on the
next couple of pages the custom property, ProcID text Reference,
ProcName text refernce, Combobox, REFRESH, and KILLPROC buttons
are shown.

148
When creating the custom properties make sure the ProcessID is an
integer. The only modifications required to the combobox is just
delete out the default list items so the list empty like you see below.

149
150
151
Step 6 : Create the ProcessList Graphic Scripts

There are only three scripts in the ProcessList Graphic, the OnShow
script and two data change scripts. The first data change script is called
GetProcName and the other is called Refresh.

The GetProcName script consists of the code in line 15 and 16 from the
OnShow script. Copy and paste those two lines and set the data change
triigger to ComboBox1.SelectedIndex.

This way every time the combobox selected index changes the text
references are updated.

The Refresh script consists of lines 11-13 of the OnSHow script,


copy and paste those lines and put them in the Refresh script then
configure the script to execute as a data change on
me.runGetProcs. This will trigger the SystemProcess object to
refresh its data arrays.
1. '###########################
2. ' OnShow load the Process
3. ' Names into the combo box
4. '
5. '#########################
6.
7.
8.
9. dim i as integer;
10.
11. for i = 1 to 300
12. ComboBox1.AddItem(Me.ProcessNames[i]);
13. next;
14.
15. ProcessName = Me.ProcessNames[ComboBox1.SelectedIndex+1];
16. ProcessID = Me.ProcessIDs[ComboBox1.SelectedIndex+1];

GitHub Code Link:


https://github.com/cbvance/StepByStep/blob/master/
ProcessListOnShow
152
Step 7 : Embedthe ProcessList Graphic

153
154
155

Вам также может понравиться