Академический Документы
Профессиональный Документы
Культура Документы
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.
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.
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
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.
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.
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.
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.
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
Vendor Documentation
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.
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.
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.
• Periodic
• Data Change
• OnTrue
• OnFalse
• WhileTrue
• WhileFalse
20
The structures addressed in this section are:
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 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;
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.
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.
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
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
32
Example 3 Script
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
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.
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
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;
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.
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.
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;
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
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 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.
Ignoring the error is useful when you are testing but is not a good idea to
leave in place after the code is completed.
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.
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;
46
https://github.com/cbvance/StepByStep/blob/master/Example7
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()
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.
48
Step 2 Add Custom Properties
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.
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.
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()
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.
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;
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.
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;
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.
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.
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.
58
String Result = StringChar(ASCII CODE) - returns the Character
corresponding to the ASCII code value passed into the function.
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().
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;
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”.
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;
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
67
30. LogMessage(StringMid(TextMessage,StartChar,Chars));
31. LogMessage("");
32.
33. 'stop script execution
34. MasterObject.Trigger12 = false;
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)
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.
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;
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.
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
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.
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.
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.
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.
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.
76
Graphic Info Client
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.
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.
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;
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
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
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 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 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.
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.
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.
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
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
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.
98
Create Custom Properties
(Make sure Tagname string is set as tag reference)
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
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)
102
103
2. Valve Template Name -$MotorizedValve
Name | CloseCmd | Attribute Type | Discrete | Access Mode | InputOutput
| Description | “Valve Close Command” | Input Source | ---
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.
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.
Use the following object references in the Owning Object property for
each symbol.
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.
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
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.
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
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
130
Step 2: Create Motorized Valve Popup Symbol
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);
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.
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 2: Create the UDAs needed for the Environment Class Type
138
Name | Drives | Data Type | String Array 10 elements | Category | User
writeable
139
Step 3 : Create the Environment Type Script
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;
141
Step 4: Deploy and Test Object Instance
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.
144
Step 3 : Create the GetProcs Script
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.
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;
147
Step 5 : Create the Process List Graphic
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.
153
154
155